Tại sao và khi nào tôi nên tạo một lớp 'tĩnh'? Mục đích của từ khóa 'tĩnh' trên các lớp là gì?


47

Các statictừ khóa trên một thành viên trong nhiều ngôn ngữ có nghĩa là bạn không nên tạo ra một thể hiện của lớp đó để có thể được tiếp cận với thành viên đó. Tuy nhiên, tôi không thấy bất kỳ lời biện minh nào để tạo nên cả một lớp static. Tại sao và khi nào tôi nên làm một lớp học static?

Tôi có được lợi ích gì khi tham gia lớp học static? Ý tôi là, sau khi khai báo một lớp tĩnh, người ta vẫn nên khai báo tất cả các thành viên mà anh ấy / cô ấy muốn có quyền truy cập mà không cần khởi tạo, cũng như tĩnh.

Điều này có nghĩa là, ví dụ, Mathlớp có thể được khai báo bình thường (không tĩnh), mà không ảnh hưởng đến cách mã nhà phát triển. Nói cách khác, làm cho một lớp tĩnh hoặc bình thường là loại minh bạch cho các nhà phát triển.


Nếu bạn đang mã hóa theo kiểu Func Prog a la Rich Hickey, điều này có thể hữu ích.
Công việc

1
Theo tôi, các lớp tĩnh chỉ là một cái nạng mà qua đó C # cố gắng che giấu một lỗ hổng thiết kế lớn trong ngôn ngữ. Tại sao cấu trúc một ngôn ngữ xung quanh hệ tư tưởng tất cả mọi thứ vô nghĩa này, chỉ sau đó giới thiệu cú pháp bổ sung để bạn có thể gian lận và giải quyết vấn đề hạn chế không cần thiết này. Nếu C # chỉ cho phép các chức năng miễn phí từ việc di chuyển, thì sẽ không cần đến các lớp tĩnh.
antred

Câu trả lời:


32

Nó làm cho nó rõ ràng cho người dùng cách lớp được sử dụng. Chẳng hạn, việc viết đoạn mã sau đây là hoàn toàn vô nghĩa:

Math m = new Math();

C # không phải cấm này nhưng vì nó phục vụ không có mục đích, cũng có thể nói cho người dùng biết. Một số người (bao gồm cả tôi) tuân thủ triết lý rằng các ngôn ngữ lập trình (và API API) phải hạn chế nhất có thể để khiến họ khó sử dụng sai: các hoạt động được phép duy nhất là những hoạt động có ý nghĩa và (hy vọng) đúng.


3
Tôi không đồng ý với triết lý đó. Vấn đề tôi có là cần thay đổi. Vì vậy, trong khi nó có thể có ý nghĩa để hạn chế nó trước đây thì hạn chế đó cản trở sự phát triển của chúng tôi cho các nhu cầu trong tương lai. Và thư viện cũ mà chúng tôi buộc phải sử dụng để giao tiếp với hệ thống cũ (Thư viện bạn đã viết và niêm phong và tĩnh tất cả các lớp) không cung cấp bất kỳ cách nào để chúng tôi mở rộng hoặc sửa đổi mã mà bạn đã viết. Chỉ vì bạn không có lý do để khởi tạo lớp học của bạn không có nghĩa là tôi sẽ không có nhu cầu về nó trong tương lai. Trong khi tĩnh một lớp tiện ích có ý nghĩa, xem xét tương lai quá.
SoylentGray

19
@Chad Sau đó, thư viện được thiết kế tồi. Việc mở rộng các lớp không được thiết kế để mở rộng cuối cùng không hoạt động, vì vậy tốt nhất hãy tạo lớp sealedngay từ đầu. Tôi thừa nhận rằng không phải tất cả mọi người đều đồng ý với tình cảm này nhưng các ý kiến ​​không phải là nơi tốt để thảo luận về vấn đề này. Nhưng trong trường hợp của staticvấn đề này, thậm chí còn không tự đặt ra: khởi tạo System.Mathsẽ không bao giờ có ý nghĩa.
Konrad Rudolph

1
@Konard, nếu bạn khai báo tất cả các thành viên của Mathlớp là tĩnh mà không khai báo Mathlớp là tĩnh, bạn vẫn có thể sử dụng nó mà không cần phải khởi tạo.
Saeed Neamati

11
@Saeed - Điều này đúng, nhưng bạn cũng có thể khởi tạo lớp, không phục vụ cho bất kỳ mục đích nào. Tôi phải làm gì với một thể hiện của lớp Toán? Để thể hiện rõ ràng ý định của lớp (không cần khởi tạo hoặc muốn), nó được đánh dấu static.
Corbin ngày

3
@Saeed Hãy quay lại vấn đề này: Tôi đề nghị bạn đọc lại câu trả lời của tôi vì bạn dường như đã hiểu nhầm: Nhận xét của bạn không liên quan đến câu trả lời của tôi. Tôi không bao giờ nói rằng bạn cần phải viết new Math().
Konrad Rudolph

24

StackOverflow có một cuộc thảo luận tuyệt vời về chủ đề này . Để dễ tham khảo, tôi sẽ sao chép và dán nó ở đây, thay mặt cho tác giả của nó, Mark S. Rasmussen :

Tôi đã viết những suy nghĩ của tôi về các lớp tĩnh trong một chủ đề trước đó :

Tôi đã từng yêu thích các lớp tiện ích chứa đầy các phương thức tĩnh. Họ đã thực hiện một sự hợp nhất tuyệt vời của các phương pháp trợ giúp mà nếu không sẽ nằm xung quanh gây ra sự dư thừa và bảo trì địa ngục. Chúng rất dễ sử dụng, không cần khởi tạo, không xử lý, chỉ cần đốt lửa. Tôi đoán đây là nỗ lực vô tình đầu tiên của tôi trong việc tạo ra một kiến ​​trúc hướng dịch vụ - rất nhiều dịch vụ phi trạng thái chỉ làm công việc của họ và không có gì khác. Khi một hệ thống phát triển, những con rồng đang đến.

Đa hình

Giả sử chúng ta có phương thức UtilityClass.SomeMethod vui vẻ reo lên. Đột nhiên chúng ta cần thay đổi chức năng một chút. Hầu hết các chức năng là như nhau, nhưng chúng ta phải thay đổi một vài phần. Nếu nó không phải là một phương thức tĩnh, chúng ta có thể tạo một lớp phái sinh và thay đổi nội dung phương thức khi cần. Vì nó là một phương pháp tĩnh, chúng ta không thể. Chắc chắn, nếu chúng ta chỉ cần thêm chức năng trước hoặc sau phương thức cũ, chúng ta có thể tạo một lớp mới và gọi lớp cũ bên trong nó - nhưng đó chỉ là thô.

Khủng hoảng giao diện

Các phương thức tĩnh không thể được định nghĩa thông qua các giao diện vì lý do logic. Và vì chúng ta không thể ghi đè các phương thức tĩnh, các lớp tĩnh là vô dụng khi chúng ta cần chuyển chúng qua giao diện của chúng. Điều này làm cho chúng ta không thể sử dụng các lớp tĩnh như là một phần của mẫu chiến lược. Chúng tôi có thể khắc phục một số vấn đề bằng cách chuyển đại biểu thay vì giao diện.

Kiểm tra

Điều này về cơ bản đi đôi với tai ương giao diện được đề cập ở trên. Vì khả năng trao đổi triển khai của chúng tôi rất hạn chế, chúng tôi cũng sẽ gặp khó khăn khi thay thế mã sản xuất bằng mã kiểm tra. Một lần nữa, chúng ta có thể gói chúng lại nhưng nó sẽ yêu cầu chúng ta thay đổi phần lớn mã của chúng ta chỉ để có thể chấp nhận các hàm bao thay vì các đối tượng thực tế.

Bồi dưỡng blobs

Vì các phương thức tĩnh thường được sử dụng làm phương thức tiện ích và phương thức tiện ích thường sẽ có các mục đích khác nhau, chúng tôi sẽ nhanh chóng kết thúc với một lớp lớn chứa đầy chức năng không kết hợp - lý tưởng là mỗi lớp nên có một mục đích duy nhất trong hệ thống. Tôi muốn có nhiều hơn năm lần các lớp miễn là mục đích của chúng được xác định rõ.

Thông số creep

Để bắt đầu, phương pháp tĩnh nhỏ dễ thương và ngây thơ đó có thể có một tham số duy nhất. Khi chức năng phát triển, một vài tham số mới được thêm vào. Các tham số tiếp theo được thêm vào là tùy chọn, vì vậy chúng tôi tạo ra sự quá tải của phương thức (hoặc chỉ thêm các giá trị mặc định, bằng các ngôn ngữ hỗ trợ chúng). Không lâu sau, chúng ta có một phương thức lấy 10 tham số. Chỉ có ba cái đầu tiên thực sự cần thiết, tham số 4-7 là tùy chọn. Nhưng nếu tham số 6 được chỉ định, 7-9 cũng được yêu cầu điền vào ... Chúng ta đã tạo một lớp với mục đích duy nhất là làm phương thức tĩnh này đã làm gì, chúng ta có thể giải quyết điều này bằng cách lấy các tham số bắt buộc trong hàm tạo và cho phép người dùng đặt các giá trị tùy chọn thông qua các thuộc tính hoặc phương thức để đặt nhiều giá trị phụ thuộc lẫn nhau cùng một lúc. Cũng thế,

Yêu cầu người tiêu dùng tạo ra một thể hiện của các lớp mà không có lý do

Một trong những đối số phổ biến nhất là, tại sao yêu cầu người tiêu dùng của lớp chúng ta tạo một thể hiện để gọi phương thức đơn này, trong khi không sử dụng ví dụ sau đó? Tạo một thể hiện của một lớp là một hoạt động rất rẻ trong hầu hết các ngôn ngữ, vì vậy tốc độ không phải là vấn đề. Thêm một dòng mã bổ sung cho người tiêu dùng là một chi phí thấp để đặt nền tảng của một giải pháp dễ bảo trì hơn nhiều trong tương lai. Và cuối cùng, nếu bạn muốn tránh tạo các thể hiện, chỉ cần tạo một trình bao bọc đơn của lớp cho phép tái sử dụng dễ dàng - mặc dù điều này làm cho yêu cầu rằng lớp của bạn không trạng thái. Nếu nó không trạng thái, bạn vẫn có thể tạo các phương thức trình bao bọc tĩnh xử lý mọi thứ, trong khi vẫn mang lại cho bạn tất cả lợi ích về lâu dài. Cuối cùng,

Chỉ có một Sith thỏa thuận trong tuyệt đối

Tất nhiên, có những ngoại lệ đối với việc tôi không thích các phương thức tĩnh. Các lớp tiện ích thực sự không gây ra bất kỳ rủi ro nào cho sự phình to là trường hợp tuyệt vời cho các phương thức tĩnh - System.Convert làm ví dụ. Nếu dự án của bạn là một lần duy nhất không có yêu cầu bảo trì trong tương lai, thì kiến ​​trúc tổng thể thực sự không quan trọng lắm - tĩnh hoặc không tĩnh, không thực sự quan trọng - tuy nhiên tốc độ phát triển.

Tiêu chuẩn, tiêu chuẩn, tiêu chuẩn!

Sử dụng các phương thức cá thể không ngăn cản bạn sử dụng các phương thức tĩnh và ngược lại. Miễn là có lý do đằng sau sự khác biệt và nó được chuẩn hóa. Không có gì tệ hơn là nhìn qua một lớp kinh doanh ngổn ngang với các phương thức thực hiện khác nhau.


12
Hmmm, không biết rằng tôi sẽ sao chép và dán toàn bộ câu trả lời ở đây. Có thể liên kết và diễn giải, bao gồm một quan điểm thay thế kể từ khi bạn đề cập đến một "cuộc thảo luận", chia sẻ kinh nghiệm của riêng bạn?
Corbin ngày

2
"Chỉ một người Sith thỏa thuận một cách tuyệt đối" - Tôi chưa từng thấy nó được sử dụng như thế trước đây .... nhưng nó HOÀN HẢO. Làm tôi lol.
Brook

4
@Corbin: Mark đã có một câu trả lời tuyệt vời. Tôi đã cho anh ta tín dụng và tôi sao chép / dán để dễ tham khảo. Quan điểm của riêng tôi về vấn đề này phù hợp với Mark's. Tôi không thấy ý kiến ​​của bạn, ngoài việc bắt đầu tranh luận.
Rick

1
@Rick: Câu trả lời được sao chép khá dài và việc diễn giải nó chắc chắn sẽ có ích. Chỉ một câu như "Không chỉ là chúng không thực sự hữu ích, thậm chí chúng còn có thể gây hại, vì bài đăng SO này cho thấy:" sẽ cho phép tôi nhanh chóng bỏ qua vì tôi thấy đó không phải là thông tin mà tôi đang tìm kiếm.
blubb

@Simon: LOL tôi 1 + 'ed bài viết của bạn. Trong một nỗ lực để bỏ cuộc tranh cãi về một cái gì đó quá ngu ngốc, tôi sẽ chỉ đồng ý. :) Hy vọng rằng các poster ban đầu tìm thấy trả lời / sao chép của tôi và dán hữu ích.
Rick

16

Tôi ngạc nhiên không ai khác đã đề cập rằng staticcác lớp cho phép các phương thức mở rộng - mở rộng một cách an toàn một loại hiện có (bao gồm thêm các định nghĩa phương thức vào các giao diện ) mà bạn không sở hữu. Chẳng hạn, những gì trong Scala là

trait MyFoo {
  def foo: Int
  def plusFoo(a: Int) = foo + a
}

có thể được thể hiện bằng C # như

public interface IMyFoo {
  int Foo();
}

public static class MyFooExtensions {
  public static int PlusFoo(this IMyFoo f, int a) {
    return f.Foo() + a;
  }
}

bạn tìm thấy kim trong đống cỏ khô. +1
Saeed Neamati

3

Đối với tôi, một lớp tĩnh (trong C #) giống như gọi ra một hàm.

Ví dụ

public static string DoSomething(string DoSomethingWithThisString){}

Tôi truyền vào một chuỗi và lấy lại một chuỗi. Tất cả mọi thứ được chứa trong phương pháp đó. Không có quyền truy cập vào biến thành viên, vv

Thành thật mà nói, phần lớn việc sử dụng nó đối với tôi là giảm bớt các dòng mã:

Tại sao:

MyClass class = new MyClass();
String s = class.DoSomething("test");

Khi tôi có thể làm điều này:

String s = MyClass.DoSomething("test");

Vì vậy, tôi sẽ sử dụng các lớp tĩnh trong các trường hợp đó, khi tôi muốn làm một cái gì đó mà không cần một lớp và có nghĩa là ít gõ hơn.

Đánh dấu S. điểm là hợp lệ, nhưng đối với tôi nếu tôi có một số phương thức tiện ích thì việc giả vờ tôi gọi một hàm để tham chiếu chúng là một lớp lót dễ dàng hơn.

Điều đó có thể đơn giản, nhưng đó chủ yếu là cách tôi đã sử dụng static.


1
MyClass mới (). DoS Something ("test") vẫn hợp lệ. Tôi sử dụng điều này rất nhiều cho các nhà xây dựng dữ liệu thử nghiệm.
JoanComasFdz
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.