Tại sao sử dụng một singleton thay vì các phương thức tĩnh?


93

Tôi chưa bao giờ tìm thấy câu trả lời hay cho những câu hỏi đơn giản này về các lớp trợ giúp / tiện ích:

Tại sao tôi lại tạo một singleton (không trạng thái) thay vì sử dụng các phương thức tĩnh?

Tại sao một thể hiện đối tượng lại cần thiết nếu một đối tượng không có trạng thái?



Đừng nghĩ rằng kể từ khi chúng ta nói về 2 langages khác nhau để câu trả lời có thể khác nhau, nhưng nhờ các liên kết, trong java chưa bao giờ nghe thuật ngữ "monostate"
Sebastien Lorber

Câu trả lời:


79

Thông thường, các singlelet được sử dụng để giới thiệu một số loại trạng thái toàn cục cho một ứng dụng. (Thành thật mà nói, thường xuyên hơn mức thực sự cần thiết, nhưng đó là một chủ đề cho thời gian khác.)

Tuy nhiên, có một số trường hợp góc mà ngay cả một singleton không trạng thái cũng có thể hữu ích:

  • Bạn mong đợi để mở rộng nó với trạng thái trong tương lai gần.
  • Bạn cần một cá thể đối tượng vì một số lý do kỹ thuật cụ thể .
    Ví dụ: Các đối tượng đồng bộ hóa cho C # lockhoặc synchronizedcâu lệnh Java .
  • Bạn cần kế thừa, tức là bạn muốn có thể dễ dàng thay thế singleton của mình bằng một cái khác bằng cách sử dụng cùng một giao diện nhưng cách triển khai khác.
    Ví dụ: Toolkit.getDefaultToolkit()Phương thức trong Java sẽ trả về một singleton có kiểu chính xác phụ thuộc vào hệ thống.
  • Bạn muốn tham chiếu bình đẳng cho một giá trị sentinel .
    Ví dụ: DBNull.Valuetrong C #.

27
Tôi sẽ chọn +1, mặc dù các đĩa đơn IMHO bị lạm dụng để giới thiệu các trạng thái toàn cầu. Mục đích của singleton không phải là làm cho một đối tượng có sẵn trên toàn cầu, mà là để thực thi đối tượng đó chỉ được khởi tạo một lần. Đối tượng toàn cầu là một điều ác cần thiết. Trừ khi thực sự bắt buộc, người ta không nên cố gắng không sử dụng chúng, vì chúng thường dẫn đến khả năng ghép nối cao, với SomeSingleton.getInstance (). SomeMethod () ở khắp nơi. :)
back2dos

Nó có thể hữu ích trong các trò chơi mà bạn chỉ muốn có một trường hợp hiển thị thay vì nhiều trường hợp hiển thị hoặc với một lớp đường ống mạng nơi bạn thiết lập một kênh an toàn mà chỉ có thể có một kết nối tại một thời điểm.
Tschallacka

2
phần "dễ dàng thay thế singleton của bạn" là điểm quan trọng nhất trong quan điểm của tôi. Phần còn lại khá gần với việc triển khai lớp tĩnh.
Teoman shipahi

1
Singleton là các giá trị sentinel không phải Null hữu ích cho các loại tổng / sản phẩm và tương tự. Ví dụ: là giá trị Không có / Không có gì trong loại Tùy chọn hoặc giá trị Rỗng cho loại tập hợp. Bạn nhận được nhanh chóng kiểm tra Không có / Trống rỗng như một phần thưởng, vì bình đẳng tham chiếu là tất cả những gì cần thiết.
itbruce

@itsbruce: Các ví dụ của bạn không phải là danh sách đơn : Danh sách trống không phải là trường hợp duy nhất có thể có của lớp Danh sách và giá trị Không có không phải là trường hợp duy nhất có thể có của lớp Option. Tuy nhiên, có một số ví dụ trong đó giá trị sentinel là các hạt đơn, và tôi đã có quyền tự do thêm một giá trị vào câu trả lời của mình. Cảm ơn vì lưu lượng!
Heinzi

37

Tôi có thể thấy một trường hợp cho một singleton không trạng thái được sử dụng thay vì một lớp phương thức tĩnh, cụ thể là cho Dependency Injection .

Nếu bạn có một lớp trợ giúp của các chức năng tiện ích mà bạn đang sử dụng trực tiếp, nó sẽ tạo ra một phụ thuộc ẩn; bạn không kiểm soát được ai có thể sử dụng nó hoặc ở đâu. Việc tiêm cùng một lớp trợ giúp đó thông qua một cá thể singleton không trạng thái cho phép bạn kiểm soát vị trí và cách nó được sử dụng và thay thế nó / giả lập nó / v.v. khi bạn cần.

Làm cho nó trở thành một cá thể singleton chỉ đơn giản là đảm bảo rằng bạn không phân bổ thêm bất kỳ đối tượng nào thuộc loại hơn mức cần thiết (vì bạn chỉ cần một).


1
"bạn không kiểm soát được ai có thể sử dụng nó hoặc ở đâu." Tại sao ai đó sẽ cần nó?
hagrawal,

1
@hagrawal cho mục đích thử nghiệm, bạn sẽ có thể để chế nhạo nó
Jemshit Iskenderov

15

Trên thực tế, tôi đã tìm thấy một câu trả lời khác không được đề cập ở đây: các phương pháp tĩnh khó kiểm tra hơn.

Có vẻ như hầu hết các khung công tác thử nghiệm đều hoạt động hiệu quả trong việc mô phỏng các phương thức phiên bản nhưng nhiều trong số chúng không xử lý một cách tốt việc mô phỏng các phương thức tĩnh.


2
Nhưng Powermock dường như có thể làm như vậy
Sebastien Lorber

6

Trong hầu hết các ngôn ngữ lập trình, các lớp loại bỏ rất nhiều kiểu hệ thống. Trong khi một lớp, với các phương thức và biến tĩnh của nó là một đối tượng, nó thường không thể triển khai một giao diện hoặc mở rộng các lớp khác. Vì lý do đó, nó không thể được sử dụng theo cách đa hình, vì nó không thể là kiểu con của một kiểu khác. Ví dụ, nếu bạn có một giao diện IFooable, được yêu cầu bởi một số ký hiệu phương thức của các lớp khác, thì đối tượng lớp StaticFookhông thể được sử dụng thay thế IFooable, ngược lại FooSingleton.getInstance()có thể (giả sử, FooSingletonthực hiện IFooable).

Xin lưu ý rằng, như tôi đã nhận xét về câu trả lời của Heinzi, một singleton là một mẫu để kiểm soát việc khởi tạo. Nó thay thế new Class()bằng Class.getInstance(), cung cấp cho tác giả Classquyền kiểm soát nhiều hơn đối với các cá thể mà anh ta có thể sử dụng để ngăn chặn việc tạo ra các cá thể không cần thiết. Singleton chỉ là một trường hợp rất đặc biệt của mô hình nhà máy và nên được xử lý như vậy. Việc sử dụng phổ biến khiến nó trở thành trường hợp đặc biệt của các sổ đăng ký toàn cầu, thường kết thúc không tốt, bởi vì các đăng ký toàn cầu không nên được sử dụng hoàn toàn.

Nếu bạn định cung cấp các hàm trợ giúp toàn cục, thì các phương thức tĩnh sẽ hoạt động tốt. Lớp sẽ không hoạt động như một lớp, mà chỉ là một không gian tên. Tôi khuyên bạn nên duy trì sự gắn kết cao, nếu không bạn có thể gặp phải các vấn đề khớp nối kỳ lạ nhất.

Greetz
back2dos


4

Có một sự đánh đổi giữa việc sử dụng cái nào. Singleton có thể có hoặc không có trạng thái và chúng chỉ các đối tượng. Nếu chúng không giữ trạng thái và chỉ được sử dụng để truy cập toàn cục, thì phương thức tĩnh sẽ tốt hơn vì các phương thức này sẽ nhanh hơn. Nhưng nếu bạn muốn sử dụng các đối tượng và các khái niệm OOP (Đa hình kế thừa), thì singleton sẽ tốt hơn.

Hãy xem xét một ví dụ: java.lang.Runtime là một lớp singleton trong java. Lớp này cho phép các triển khai khác nhau cho mỗi JVM. Việc triển khai là duy nhất cho mỗi JVM. Nếu lớp này là tĩnh, chúng ta không thể chuyển các triển khai khác nhau dựa trên JVM.

Tôi thấy liên kết này thực sự hữu ích: http://javarevisited.blogspot.com/2013/03/difference-between-singleton-pattern-vs-static-class-java.html ?

Hy vọng nó giúp!!


Câu trả lời này rất phù hợp để bao gồm một ví dụ cụ thể trong thế giới thực.
systemovich

2

Đối với tôi "Muốn Trạng thái đối tượng sử dụng Singleton, Muốn Hàm sử dụng phương thức tĩnh"

Nó phụ thuộc vào những gì bạn muốn. Bất cứ khi nào bạn muốn trạng thái đối tượng (ví dụ: Đa hình như trạng thái Null thay vì null, hoặc trạng thái mặc định), singleton là lựa chọn thích hợp cho bạn trong khi phương thức tĩnh sử dụng khi bạn cần hàm (Nhận đầu vào rồi trả về đầu ra).

Tôi khuyến nghị đối với trường hợp singleton, nó phải luôn ở cùng một trạng thái sau khi nó được khởi tạo. Nó không được sao chép hoặc nhận bất kỳ giá trị nào để đặt vào (ngoại trừ cấu hình tĩnh từ tệp, ví dụ tệp thuộc tính trong java).

PS Hiệu suất giữa 2 thứ này khác nhau tính bằng mili giây, vì vậy hãy tập trung vào Kiến trúc trước.


1

Singleton không phải là không trạng thái, nó giữ trạng thái toàn cầu.

Một số lý do mà tôi có thể nghĩ đến việc sử dụng Singleton là:

  • Để tránh rò rỉ bộ nhớ
  • Để cung cấp cùng một trạng thái cho tất cả các mô-đun trong một ứng dụng, ví dụ như kết nối cơ sở dữ liệu

Tôi biết nhưng thực sự là một singleton có thể nhiều hơn hoặc ít được quốc tịch ... Nếu nó không chia sẻ bất kỳ thuộc tính lớp ...
Sebastien Lorber
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.