Đừng sử dụng Tĩnh Tĩnh trong C #?


109

Tôi đã gửi một ứng dụng tôi đã viết cho một số kiến ​​trúc sư khác để xem xét mã. Một trong số họ gần như ngay lập tức viết lại cho tôi và nói "Đừng sử dụng" tĩnh ". Bạn không thể viết các bài kiểm tra tự động với các lớp và phương thức tĩnh." Tĩnh "là phải tránh."

Tôi đã kiểm tra và đầy đủ 1/4 các lớp học của tôi được đánh dấu "tĩnh". Tôi sử dụng tĩnh khi tôi sẽ không tạo một thể hiện của một lớp vì lớp này là một lớp toàn cầu duy nhất được sử dụng trong toàn bộ mã.

Ông tiếp tục đề cập đến một cái gì đó liên quan đến chế nhạo, các kỹ thuật IOC / DI không thể được sử dụng với mã tĩnh. Ông nói rằng thật không may khi các thư viện bên thứ 3 tĩnh vì không thể kiểm tra được.

Đây có phải là kiến ​​trúc sư khác đúng không?

cập nhật: đây là một ví dụ:

APIManager - lớp này lưu giữ từ điển API của bên thứ 3 mà tôi đang gọi cùng với thời gian được phép tiếp theo. Nó thực thi các giới hạn sử dụng API mà rất nhiều bên thứ 3 có trong điều khoản dịch vụ của họ. Tôi sử dụng nó ở bất cứ đâu tôi đang gọi dịch vụ của bên thứ 3 bằng cách gọi Thread.S ngủ (APIManager.GetWait ("CarrierXYZ")); trước khi thực hiện cuộc gọi. Mọi thứ ở đây đều an toàn và nó hoạt động rất tốt với TPL trong C #.


37
staticỔn; static các lĩnh vực cần được xử lý rất cẩn thận
Marc Gravell

2
Tại sao anh ta viết bài kiểm tra cho các thư viện bên thứ 3? Bạn không thường xuyên kiểm tra mã của riêng bạn giả sử những người tạo thư viện bên thứ 3 đã thực hiện thử nghiệm của họ?
Không có

3
Sự phản đối đặc biệt đó là hơi quá chung chung đối với tôi. Trong hầu hết các trường hợp, bạn sẽ không tạo ra lớp tĩnh, bạn sẽ chế giễu các đối số. Trường tĩnh nơi nó là một lớp tổng hợp cần chế nhạo, thì có.

8
Rõ ràng các đồng nghiệp của bạn có một tình trạng nghiêm trọng - một bệnh OOPus ban đỏ hệ thống cấp tính. Họ cần điều trị càng sớm càng tốt!
SK-logic

6
Có một phần câu trả lời để cung cấp câu trả lời, tôi không hiểu tại sao mọi người bình luận trong nỗ lực đưa ra câu trả lời / đề nghị cho ai đó ... Nó đánh bại mục đích của StackExchange, chắc chắn một bình luận sẽ yêu cầu thêm thông tin.
peteski

Câu trả lời:


121

Nó phụ thuộc vào việc các lớp tĩnh có duy trì trạng thái hay không. Cá nhân, tôi không có vấn đề gì với các hàm không trạng thái được ném vào nhau trong một lớp tĩnh.


111
Chính xác. Các hàm tĩnh không có tác dụng phụ là các hàm có thể kiểm tra nhất.
Robert Harvey

9
+1 với điều đó, nhưng với một mệnh đề an toàn: Hầu như mọi thuật toán trừu tượng đều không trạng thái, nhưng điều đó không có nghĩa là bạn nên triển khai nó dưới dạng lớp hoặc phương thức tĩnh không trạng thái. Việc triển khai nó theo cách như vậy làm cho tất cả các mã sử dụng nó hầu như không được liên kết với nó. Ví dụ, điều này có nghĩa là nếu bạn triển khai thuật toán sắp xếp như một phương thức tĩnh và sử dụng nó thông qua dự án - rằng bạn không thể tạm thời thay thế việc sắp xếp sang thuật toán khác, ví dụ cho mục đích thử nghiệm và thu hẹp khu vực vấn đề. Đây là một trở ngại rất lớn trong nhiều trường hợp. Bên cạnh đó, tĩnh hoàn toàn ổn nếu được sử dụng hợp lý.

11
Nói tóm lại: chúng là các hàm có thể kiểm tra nhất, nhưng không phải là hàm, chúng làm cho các mã khác dễ kiểm tra hơn! Đây là những gì kiến ​​trúc sư có thể có nghĩa.

4
@ tereško: Ngôn ngữ C # yêu cầu các phương thức tĩnh là một phần của lớp tĩnh, nếu bạn không muốn phải tạo một thể hiện của lớp để gọi phương thức. Có lẽ bạn có nghĩa là "cá thể" chứ không phải "đẳng cấp".
Robert Harvey

8
@quetzalcoatl: Hoàn toàn hợp pháp khi chuyển một đại biểu (tức là một thuật toán khác) cho một phương thức tĩnh; các phương thức mở rộng trong Linq đều là các phương thức tĩnh. Ví dụ: msdn.microsoft.com/en-us/l Library / Google
Robert Harvey

93

Anh ấy quá chung chung về nó. Ông là chính xác, nó cản trở thử nghiệm. Tuy nhiên, staticcác lớp và phương thức có vị trí của chúng và nó thực sự phụ thuộc vào ngữ cảnh. Không có ví dụ mã bạn không thể thực sự nói.

Tôi sử dụng tĩnh khi tôi sẽ không tạo một thể hiện của một lớp vì lớp này là một lớp toàn cầu duy nhất được sử dụng trong toàn bộ mã.

Đây có thể là mùi mã nghiêm trọng. Bạn đang sử dụng các lớp học để làm gì? Để giữ dữ liệu? Để truy cập cơ sở dữ liệu? Rồi anh đúng. Bạn nên xem xét việc tiêm phụ thuộc trong trường hợp đó, vì một lớp tĩnh như vậy thực sự là một singleton ẩn.

Nếu bạn đang sử dụng chúng cho các phương thức mở rộng hoặc người trợ giúp không thay đổi trạng thái và chỉ hoạt động dựa trên các tham số bạn cung cấp, thì chúng thường ổn.


3
+1 để đề cập rằng tuyên bố là nói chung và đề cập đến các phương pháp mở rộng, cũng có thể được kiểm tra và các phụ thuộc vẫn có thể bị chế giễu theo như tôi biết.
Không có

4
Aye tĩnh như một thể hiện của lớp đơn lẻ như một cá thể ngầm, điều đó sẽ trở nên lộn xộn ....

"Để truy cập cơ sở dữ liệu?" tại sao không ? nếu bộ điều hợp bảng không tĩnh thì tạo một lớp tĩnh @ tập dữ liệu được gõ mạnh, không có gì sai với nó .... trừ khi bạn chứng minh quan điểm của mình bằng tham chiếu hoặc ví dụ?
Toán học

27

Tôi đã kiểm tra và đầy đủ 1/4 các lớp học của tôi được đánh dấu "tĩnh". Tôi sử dụng tĩnh khi tôi sẽ không tạo một thể hiện của một lớp vì lớp này là một lớp toàn cầu duy nhất được sử dụng trong toàn bộ mã.

Điều tốt nhất để làm là thử và kiểm tra đơn vị mã của bạn. Hãy thử thiết kế các bài kiểm tra có thể lặp lại, độc lập, đơn giản và chỉ kiểm tra một phương pháp tại một thời điểm. Hãy thử chạy thử nghiệm của bạn theo thứ tự ngẫu nhiên khác nhau. Bạn có thể có được bản dựng "xanh" ổn định không?

Nếu có, đó là một điểm hợp lệ để bảo vệ mã của bạn. Tuy nhiên, nếu bạn gặp khó khăn, thì có lẽ bạn nên dùng các phương pháp dựa trên ví dụ.


7
Đây là điểm mà đánh tôi quá. Một vài lớp phong cách 'người trợ giúp' tĩnh và không trạng thái là tốt, nhưng nếu bạn có đầy đủ 25% tất cả các lớp là tĩnh, thì những nỗ lực của bạn không bị giới hạn trong trường hợp đó.
Matthew Scharley

2
@MatthewScharley - anh ta có thể có 4 lớp và 1 trong số đó là Tĩnh :)
Alexus

1
Xem ra rằng nếu bạn sử dụng các stibcs như vậy (như singletons), thì việc tạo ra nhiều phiên bản sau có thể là một công việc khó khăn. Giống như tạo một ứng dụng xử lý tài liệu, bạn sẽ gặp vấn đề sau này khi bạn muốn quản lý nhiều tài liệu.
Michel Keijzers

11

Các câu trả lời đã được đăng bao gồm rất nhiều điểm thực sự tốt, nhưng có một điểm dường như còn thiếu:

Các trường tĩnh không bao giờ được thu gom rác.

Điều này thực sự quan trọng nếu bạn có một ứng dụng có nhiều hạn chế về bộ nhớ và mẫu này có thể rất phổ biến khi mọi người cố gắng thực hiện bộ đệm.

Các hàm tĩnh gần như không tệ, nhưng mọi người khác đã trình bày chi tiết này đủ chi tiết.


10

Một trong những lợi thế mà bạn nhận được từ IoC / DI là hầu hết các tương tác giữa các lớp được đàm phán giữa các giao diện. Điều này làm cho việc kiểm tra đơn vị dễ dàng vì các giao diện có thể được mô phỏng tự động hoặc bán tự động, và do đó mỗi bộ phận có thể được kiểm tra cho đầu vào và đầu ra. Hơn nữa, ngoài khả năng kiểm tra, việc đặt giao diện giữa mọi thứ cho phép bạn có mã mô-đun nhất có thể - bạn có thể dễ dàng thay thế một triển khai mà không quá lo lắng rằng bạn đang làm hỏng phụ thuộc.

Vì C # không có siêu dữ liệu, nên một lớp không thể thực hiện giao diện bằng các tính năng tĩnh, do đó, các tính năng tĩnh của các lớp kết thúc mọi nỗ lực để thực hiện mô hình đối tượng IoC / DI thuần túy. Điều đó có nghĩa là, bạn không thể tạo ra một bản giả để kiểm tra chúng và bạn phải tạo ra sự phụ thuộc thực sự.

Nếu công ty / dự án của bạn được đầu tư rất nhiều vào việc thực hiện IoC, thì điều này tất nhiên là một mối quan tâm hợp lý và nỗi đau bạn trải qua là vì lợi ích của tất cả. Tuy nhiên, từ quan điểm kiến ​​trúc, cá nhân tôi không nghĩ rằng bất kỳ mô hình nào nên được theo dõi đến ngôi mộ. Có một số điều hợp lý khi thực hiện bằng các phương thức tĩnh - ví dụ như chiến lược thiết kế singleton. Bản thân tôi ít nghiêng về các lớp tĩnh hơn vì tôi nghĩ rằng chúng có xu hướng bắt đầu mất đi những lợi thế của OO, nhưng có những lúc, một lớp thư viện có lẽ là biểu hiện tự nhiên của một thứ gì đó hơn là một động cơ.


Bình luận viên nhắc nhở tôi về các phương thức mở rộng, tất nhiên phải có trong các lớp tĩnh trong C #. Điều đó hợp pháp và là một ví dụ về cách IoC thuần túy không hoạt động tốt trong C #, ít nhất là nếu bạn đang cố gắng tận dụng lợi thế của ngôn ngữ.


1
Nếu một lớp được tạo tĩnh vì các lý do chính xác và được triển khai chính xác, chẳng hạn như các phương thức mở rộng, chúng vẫn có thể kiểm tra được rất nhiều vì chúng sẽ không có phụ thuộc bên ngoài và được khép kín và do đó không cần phải chế nhạo. Có nên yêu cầu giao diện không được điều khiển bởi yêu cầu thiết kế để đáp ứng đầy đủ nhất một yêu cầu kinh doanh không vì mục đích duy trì dự án IoC / DI thuần túy?
Không có

1
Bạn và tôi đồng ý về quan điểm chung rằng bạn nên chọn công cụ phù hợp cho công việc phù hợp và không bị còng tay bởi triết lý. Tuy nhiên, nếu có một nền văn hóa được thiết lập tốt trong dự án IoC / DI thuần túy của bạn, nó sẽ gây hại không kém gì khi chuyển đổi một giải pháp từ trên xuống truyền thống sang IoC / DI. Kỹ thuật cần xem xét các chi phí chuyển tiếp. Nhân tiện, tôi không nghĩ các phương pháp mở rộng khá đưa bạn đến đó. Tôi nghĩ rằng một giải pháp tốt hơn cho hành vi IoCish với các lớp tĩnh sẽ là có nhiều lớp được chọn giữa lúc biên dịch với các chỉ thị tiền xử lý, kiểu C
jwrush

1
Ơ Tôi ngốc thật. Bạn có nghĩa là một lớp tĩnh cho các phương thức mở rộng GIỮ, tất nhiên, đó là cách bạn phải thực hiện chúng. Vâng, tất nhiên, bạn muốn một lớp tĩnh cho mục đích đó. Điều đó đã đánh trượt tâm trí của tôi: và điều này cho thấy C # không dành cho IoC. :)
jwrush

Vâng, bạn đã đúng, nếu một nền văn hóa hiện có trong một dự án hiện có tồn tại, nó sẽ gây hại nhiều hơn là giúp thay đổi cách mọi thứ được thực hiện.
Không có

5

Các lớp tĩnh đôi khi bị sử dụng sai và nên:

  • một singleton (trong đó lớp không tĩnh có thành viên tĩnh và biến Instance tĩnh công khai để truy xuất / tạo nó chỉ một lần.
  • một phương thức trong một lớp khác (trong đó nhà nước quan trọng). Nếu bạn có một thành viên đang sử dụng không có dữ liệu từ lớp đó, có lẽ nó không phải là một phần của lớp đó.

Nó cũng có thể là một chức năng được gọi là 'tiện ích' và là một phần của lớp tiện ích. Các lớp tiện ích là các lớp chỉ chứa các phương thức tĩnh và đóng vai trò là các hàm trợ giúp mà không có bất kỳ ngữ cảnh nào.


@topomorto Bởi vì lớp không được khởi tạo bởi hàm tạo của nó dẫn đến nhiều đối tượng / thể hiện, nhưng bằng một phương thức đặc biệt (thường là thể hiện ()) <trả về luôn luôn cùng một thể hiện, được khởi tạo sau lệnh gọi đầu tiên đến cá thể ().
Michel Keijzers

@topomorto Tôi vừa kiểm tra và bạn hoàn toàn đúng, chỉ một số thành viên là tĩnh (biến thể hiện và hàm thể hiện). Tôi sẽ thay đổi câu trả lời của tôi cho phù hợp; cảm ơn đã đề cập.
Michel Keijzers

4

Các phương thức tĩnh là tốt để sử dụng và có một vị trí hợp lý trong lập trình. Có vẻ như kiến ​​trúc sư của bạn có một khung kiểm tra không hỗ trợ đầy đủ các phương thức tĩnh. Nếu mã của bạn là một phần của dự án lớn hơn, thì điều quan trọng là phải đáp ứng các hướng dẫn của kiến ​​trúc sư. Tuy nhiên, đừng để dự án này ngăn cản bạn sử dụng các phương thức tĩnh khi thích hợp để làm như vậy.


1

Tôi đã sử dụng các thuộc tính tĩnh cho những thứ phổ biến cho tất cả các thể hiện của một lớp. Và tôi đã sử dụng các phương thức tĩnh để có được các nhóm đối tượng lớp. Tôi không có nghĩa là một chuyên gia nhưng điều này đã làm việc cho tôi cho đến nay.

Ví dụ về PHP:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}

1

Tôi sẽ nói các nguyên tắc họ có đều ổn nhưng tuyên bố (không sử dụng số liệu thống kê) có thể sai. Bảo hiểm mã của bạn là bao nhiêu? nếu số lượng cao và bạn cảm thấy thoải mái với thử nghiệm đơn vị của ứng dụng của mình, thì bạn vẫn ổn. Nếu không, sau đó tôi sẽ đề nghị xem lại mã. Bạn có thể tìm thấy các lớp tĩnh là lý do hay không, nó sẽ phụ thuộc vào rất nhiều thứ.


-3

Các lớp với các phương thức tĩnh lạm dụng các nguyên tắc của OOP. Nó chưa phải là OOP, đó là COP - lập trình hướng lớp.

Họ hiếm khi có "tính cách" rõ ràng (tôi sử dụng phép ẩn dụ con người yêu thích của tôi ở đây), hoặc danh tính. Vì vậy, họ không biết họ là ai. Chỉ riêng điểm này là đủ để loại bỏ khái niệm này trong OOP, nhưng có nhiều hơn trong số họ.

Điều tiếp theo là hậu quả của điểm đầu tiên. Vì những lớp như vậy không biết họ là ai, nên họ có xu hướng từ lớn đến lớn.

Thứ ba làm cho mã của bạn hoàn toàn không thể bảo trì. Việc sử dụng các phương pháp tĩnh giới thiệu các phụ thuộc ẩn . Chúng bật lên khắp nơi và bạn không bao giờ biết chuyện gì đang xảy ra trong lớp của mình. Bạn không thể chắc chắn điều đó chỉ bằng cách nhìn vào chữ ký của nhà xây dựng.

Dần dần, nhưng chắc chắn, mã của bạn ngày càng ít gắn kết hơn, vì các phụ thuộc ẩn được kiểm soát kém. Họ dường như vô hình. Và thật dễ dàng để thêm một cái khác! Bạn không phải sửa đổi chữ ký của phương thức nào. Tất cả bạn phải làm là gọi một phương thức tĩnh. Và những gì một mã xấu xí theo sau ...

Ok, có lẽ bạn đã đoán ra. Khớp nối chặt chẽ là một tiếp viên của sự gắn kết thấp. Nếu có nhiều khách hàng sử dụng một số lớp, thì khớp nối ngày càng chặt chẽ hơn. Và có một sự quyến rũ lớn trong việc sử dụng một lớp hoặc phương thức đã được viết mà chỉ cần sửa chữa nhỏ. Chỉ có một tham số cờ phương thức.

Sử dụng gì thay vì phương thức tĩnh? Vâng, đề nghị của tôi là OOP. Tại sao không thử?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.