Khi nào bạn nên sử dụng mẫu singleton thay vì một lớp tĩnh? [đóng cửa]


85

Đặt tên cho những cân nhắc thiết kế trong việc quyết định giữa việc sử dụng của một singleton so với một lớp tĩnh. Khi làm điều này, bạn buộc phải đối chiếu cả hai, vì vậy bất cứ điều gì tương phản bạn có thể đưa ra cũng rất hữu ích trong việc thể hiện quá trình suy nghĩ của bạn! Ngoài ra, mọi người phỏng vấn đều thích xem các ví dụ minh họa. :)


một lớp java không thể tạo tĩnh trừ khi nó là một lớp bên trong
Harshana

1
Nếu trạng thái của đối tượng là quan trọng, thì hãy sử dụng singleton, nếu không, hãy sử dụng một lớp tĩnh.
Levent Divilioglu

Câu trả lời:


80
  • Singletons có thể triển khai các giao diện và kế thừa từ các lớp khác.
  • Singleton có thể được tải chậm. Chỉ khi nó thực sự cần thiết. Điều đó rất hữu ích nếu quá trình khởi tạo bao gồm tải tài nguyên hoặc kết nối cơ sở dữ liệu đắt tiền.
  • Singletons cung cấp một đối tượng thực tế.
  • Singletons có thể được mở rộng thành một nhà máy. Quản lý đối tượng đằng sau hậu trường là trừu tượng vì vậy nó có thể bảo trì tốt hơn và dẫn đến mã tốt hơn.

2
Hãy xem thêm liên kết này: codeofdoom.com/wordpress/2008/04/20/…
Amit

4
"Singletons có thể được tải lười biếng" - trong C #, hàm tạo tĩnh chỉ được gọi lần đầu tiên một thành viên tĩnh được tham chiếu. Trong PHP, bạn có thể sử dụng tính năng tự động tải để chỉ chạy trong lần đầu tiên lớp được sử dụng.
mpen

Các khởi tạo tĩnh Java cũng lười biếng. Không có điểm cho Singletons ở đó.
MikeFHay

13

Làm thế nào về "tránh cả hai"? Singleton và các lớp tĩnh:

  • Có thể giới thiệu trạng thái toàn cầu
  • Kết hợp chặt chẽ với nhiều lớp khác
  • Ẩn phụ thuộc
  • Có thể làm cho các lớp thử nghiệm đơn vị trở nên khó khăn

Thay vào đó, hãy xem xét Sự phụ thuộc InjectionInversion of Control Container các thư viện . Một số thư viện IoC sẽ xử lý việc quản lý suốt đời cho bạn.

(Như mọi khi, vẫn có những ngoại lệ, chẳng hạn như các lớp toán tĩnh và các phương thức mở rộng C #.)


1
Các nits nhỏ: các lớp singleton tương thích với các kỹ thuật tiêm phụ thuộc (chỉ cần đưa chúng vào các lớp phụ thuộc của chúng và chúng không cần phải mã hóa bất cứ thứ gì) - điều này cho phép chế nhạo và thử nghiệm một cách riêng biệt. Chúng cũng là một mẫu thiết kế hợp lệ để quản lý các tài nguyên được biết là có giới hạn trên toàn nền tảng. (Ví dụ cổ điển là máy in không có quản lý tranh chấp.)
cdleary

@cdleary - đã đồng ý; tuy nhiên việc triển khai cổ điển thường để lại Singleton.getInstance () trong suốt codebase. "Singletons" để quản lý tài nguyên, bộ nhớ đệm, v.v. có thể được triển khai với POCOs / POJOs và IoC Container framework hỗ trợ quản lý trọn đời (đăng ký kiểu làm thời gian tồn tại của vùng chứa và mọi giải pháp sẽ nhận được cùng một trường hợp).
TrueWill

9
Nếu bạn muốn chôn cất những người Singleton của mình một cách thích hợp, hãy truy cập singletonfu
tangservice.com

1
Hide Dependenciesmột chút thông tin bên lề về điểm này: Bạn có thể triển khai mẫu Inversion of Control chẳng hạn bằng cách sử dụng Ninject . Điều này cho phép bạn chỉ sử dụng một thể hiện của một kiểu bằng cách liên kết nó với a SingletonScope, có nghĩa là nó sẽ luôn đưa vào cùng một thể hiện, nhưng lớp sẽ không phải là singleton.
LuckyLikey

8

Tôi cho rằng sự khác biệt duy nhất là cú pháp: MySingleton.Current.W Anything () so với MySingleton.W Anything (). Trạng thái, như David đã đề cập, cuối cùng là "tĩnh" trong cả hai trường hợp.


CHỈNH SỬA: Lữ đoàn chôn cất đến từ digg ... dù sao thì, tôi đã nghĩ ra một trường hợp cần một singleton. Các lớp tĩnh không thể kế thừa từ một lớp cơ sở cũng như không triển khai một giao diện (ít nhất là trong .Net chúng không thể). Vì vậy, nếu bạn yêu cầu chức năng này thì bạn phải sử dụng một singleton.


7

Một trong những cuộc thảo luận yêu thích của tôi về vấn đề này là ở đây (trang web ban đầu bị gỡ xuống, hiện được liên kết với Máy lưu trữ Internet .)

Để tóm tắt các ưu điểm linh hoạt của Singleton:

  • một Singleton có thể dễ dàng chuyển đổi thành một nhà máy
  • một Singleton có thể dễ dàng sửa đổi để trả về các lớp con khác nhau
  • điều này có thể dẫn đến một ứng dụng dễ bảo trì hơn

5

Một lớp tĩnh có tải các biến tĩnh là một vấn đề khó.

/**
 * Grotty static semaphore
 **/
 public static class Ugly {

   private static int count;

   public synchronized static void increment(){
        count++;
   }

   public synchronized static void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized static boolean isClear(){
         return count==0;    

    }
   }

Một singleton với một phiên bản thực tế thì tốt hơn.

/**
 * Grotty static semaphore
 **/
 public static class LessUgly {
   private static LessUgly instance;

   private int count;

   private LessUgly(){
   }

   public static synchronized getInstance(){
     if( instance==null){
        instance = new LessUgly();
     }
     return instance;
   }
   public synchronized void increment(){
        count++;
   }

   public synchronized void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized boolean isClear(){
         return count==0;    

    }
   }

Trạng thái CHỈ ở trong trường hợp.

Vì vậy, singleton có thể được sửa đổi sau đó để thực hiện các trường hợp gộp, luồng cục bộ, v.v. Và không mã nào trong số các mã đã được viết sẵn cần phải thay đổi để nhận được lợi ích.

public static class LessUgly {
       private static Hashtable<String,LessUgly> session;
       private static FIFO<LessUgly> freePool = new FIFO<LessUgly>();
       private static final POOL_SIZE=5;
       private int count;

       private LessUgly(){
       }

       public static synchronized getInstance(){
         if( session==null){
            session = new Hashtable<String,LessUgly>(POOL_SIZE);
            for( int i=0; i < POOL_SIZE; i++){
               LessUgly instance = new LessUgly();  
               freePool.add( instance)
            }
         }
         LessUgly instance = session.get( Session.getSessionID());
         if( instance == null){
            instance = freePool.read();
         }
         if( instance==null){
             // TODO search sessions for expired ones. Return spares to the freePool. 
             //FIXME took too long to write example in blog editor.
         }
         return instance;
       }     

Có thể làm điều gì đó tương tự với một lớp tĩnh nhưng sẽ có chi phí cho mỗi cuộc gọi trong việc gửi gián tiếp.

Bạn có thể lấy thể hiện và chuyển nó vào một hàm làm đối số. Điều này cho phép mã được chuyển hướng đến singleton "bên phải". Chúng tôi biết bạn sẽ chỉ cần một trong số đó ... cho đến khi bạn không cần.

Lợi ích lớn là các singleton trạng thái có thể được tạo thành luồng an toàn, trong khi một lớp tĩnh không thể, trừ khi bạn sửa đổi nó thành một singleton bí mật.


4

Hãy nghĩ về một singleton giống như một dịch vụ. Đó là một đối tượng cung cấp một bộ chức năng cụ thể. Ví dụ

ObjectFactory.getInstance().makeObject();

Nhà máy đối tượng là một đối tượng thực hiện một dịch vụ cụ thể.

Ngược lại, một lớp chứa đầy các phương thức tĩnh là một tập hợp các hành động mà bạn có thể muốn thực hiện, được tổ chức trong một nhóm có liên quan (Lớp). Ví dụ

StringUtils.reverseString("Hello");
StringUtils.concat("Hello", "World");

Ví dụ về StringUtils ở đây là một tập hợp các chức năng có thể được áp dụng ở mọi nơi. Đối tượng nhà máy singleton là một loại đối tượng cụ thể với trách nhiệm rõ ràng có thể được tạo và chuyển đi xung quanh nơi cần thiết.


4

Các lớp tĩnh được khởi tạo trong thời gian chạy. Điều này có thể tốn thời gian. Singleton chỉ có thể được khởi tạo khi cần thiết.


13
Singleton cũng được khởi tạo trong thời gian chạy ...
đệ quy

1
@recursive: Có thể kiểm soát việc khởi tạo Singleton.
Nikit Batale,

3
có lẽ một từ tốt hơn sẽ được khởi tạo (chứ không phải instantiation)
Marlon

3

Singleton không nên được sử dụng giống như các lớp tĩnh. Trong essense,

MyStaticClass.GetInstance().DoSomething();

về cơ bản giống như

MyStaticClass.DoSomething();

Những gì bạn thực sự nên làm là coi singleton như một đối tượng khác . Nếu một dịch vụ yêu cầu một thể hiện của kiểu singleton, thì hãy chuyển thể hiện đó trong hàm tạo:

var svc = new MyComplexServce(MyStaticClass.GetInstance());

Dịch vụ không nên biết rằng đối tượng là một đơn vị, và nên coi đối tượng chỉ là một đối tượng.

Đối tượng chắc chắn có thể được thực hiện, như một chi tiết triển khai và như một khía cạnh của cấu hình tổng thể, như một đơn vị nếu điều đó làm cho mọi thứ dễ dàng hơn. Nhưng những thứ sử dụng đối tượng không nên biết đối tượng đó có phải là một singleton hay không.


2

Nếu theo "lớp tĩnh", bạn có nghĩa là một lớp chỉ có các biến tĩnh, thì chúng thực sự có thể duy trì trạng thái. Sự hiểu biết của tôi là sự khác biệt duy nhất sẽ là cách bạn truy cập vào thứ này. Ví dụ:

MySingleton().getInstance().doSomething();

đấu với

MySingleton.doSomething();

Nội bộ của MySingleton rõ ràng sẽ khác nhau giữa chúng nhưng, bỏ qua vấn đề an toàn luồng, cả hai đều sẽ hoạt động giống nhau liên quan đến mã máy khách.


2
Bản thân các Singleton là các đối tượng -> chúng có thể được truyền dưới dạng đối số.
Christian Klauser

2

Mẫu Singleton thường được sử dụng để phục vụ dữ liệu tĩnh hoặc độc lập thể hiện trong đó nhiều luồng có thể truy cập dữ liệu cùng một lúc. Một ví dụ có thể là mã tiểu bang.


1

Singleton không bao giờ được sử dụng (trừ khi bạn coi một lớp không có trạng thái có thể thay đổi là một singleton). "static class" không được có trạng thái có thể thay đổi, ngoại trừ có lẽ là bộ nhớ đệm an toàn cho luồng và những thứ tương tự.

Khá nhiều ví dụ về singleton cho thấy cách không làm điều đó.


4
Điều gì về các lớp quản lý kiểm soát tài nguyên phần cứng? Còn các lớp tệp nhật ký thì sao?
RJFalconer

0

Nếu một singleton là thứ bạn có thể vứt bỏ, để dọn dẹp sau nó, bạn có thể xem xét nó khi đó là một tài nguyên hạn chế (tức là chỉ có 1 trong số nó) mà bạn không cần luôn luôn và có một số loại bộ nhớ hoặc chi phí tài nguyên khi nó được phân bổ.

Mã dọn dẹp trông tự nhiên hơn khi bạn có một singleton, trái ngược với một lớp tĩnh chứa các trường trạng thái tĩnh.

Tuy nhiên, mã sẽ trông giống nhau vì vậy nếu bạn có lý do cụ thể hơn để hỏi, có lẽ bạn nên giải thích.


0

Cả hai có thể khá giống nhau, nhưng hãy nhớ rằng bản thân Singleton thực sự phải được khởi tạo (cấp, một lần) và sau đó được phục vụ. Một lớp cơ sở dữ liệu PHP trả về một phiên bản củamysqli hiện không thực sự là một Singleton (như một số người gọi nó), vì nó đang trả về một thể hiện của một lớp khác, không phải là một thể hiện của lớp có thể hiện là thành viên tĩnh.

Vì vậy, nếu bạn đang viết một lớp mới mà bạn dự định chỉ cho phép một phiên bản trong mã của mình, bạn cũng có thể viết nó dưới dạng Singleton. Hãy coi nó như là viết một lớp đơn giản và thêm vào nó để tạo điều kiện cho yêu cầu khởi tạo một lần. Nếu bạn đang sử dụng lớp của người khác mà bạn không thể sửa đổi (như mysqli), bạn nên sử dụng một lớp tĩnh (ngay cả khi bạn không đặt trước định nghĩa của nó bằng từ khóa).


0

Singleton linh hoạt hơn, có thể hữu ích trong trường hợp bạn muốn phương thức Instance trả về các lớp con cụ thể khác nhau của kiểu Singleton dựa trên một số ngữ cảnh.


0

Các lớp tĩnh không thể được chuyển xung quanh dưới dạng đối số; các trường hợp của một singleton có thể được. Như đã đề cập trong các câu trả lời khác, hãy để ý các vấn đề phân luồng với các lớp tĩnh.

rp


0

Một singleton có thể có một hàm tạo và hàm hủy. Tùy thuộc vào ngôn ngữ của bạn, hàm tạo có thể được gọi tự động vào lần đầu tiên singleton của bạn được sử dụng, hoặc không bao giờ nếu singleton của bạn hoàn toàn không được sử dụng. Một lớp tĩnh sẽ không có khởi tạo tự động như vậy.

Một khi tham chiếu đến một đối tượng singleton được thu thập, nó có thể được sử dụng giống như bất kỳ đối tượng nào khác. Mã máy khách thậm chí có thể không cần biết nó bằng cách sử dụng một singleton nếu tham chiếu đến singleton được lưu trữ trước đó trên:

Foo foo = Foo.getInstance();
doSomeWork(foo); // doSomeWork wont even know Foo is a singleton

Điều này rõ ràng làm cho mọi thứ dễ dàng hơn khi bạn chọn bỏ mẫu Singleton để chuyển sang mẫu thực, như IoC.


0

Sử dụng mẫu singleton khi bạn cần tính toán một cái gì đó trong thời gian chạy mà bạn sẽ tính toán lúc biên dịch nếu bạn có thể, chẳng hạn như bảng tra cứu.


0

Tôi nghĩ rằng một nơi mà Singleton sẽ có ý nghĩa hơn so với lớp tĩnh là khi bạn phải xây dựng một nhóm tài nguyên tốn kém (như kết nối cơ sở dữ liệu). Bạn sẽ không quan tâm đến việc tạo nhóm nếu không ai sử dụng chúng (lớp tĩnh sẽ có nghĩa là bạn thực hiện công việc tốn kém khi lớp được tải).


0

Một singleton cũng là một ý tưởng hay nếu bạn muốn tải dữ liệu vào bộ nhớ đệm hiệu quả. ví dụ, tôi có một lớp tìm kiếm các định nghĩa trong tài liệu xml. Vì quá trình phân tích cú pháp tài liệu có thể mất một chút thời gian, tôi đã thiết lập bộ nhớ cache các định nghĩa (tôi sử dụng SoftRefferences để tránh outOfmemeoryErrors). Nếu định nghĩa mong muốn không có trong bộ nhớ cache, tôi thực hiện phân tích cú pháp xml đắt tiền. Nếu không, tôi trả lại một bản sao từ bộ nhớ cache. Vì có nhiều bộ đệm có nghĩa là tôi vẫn có thể phải tải cùng một định nghĩa nhiều lần, nên tôi cần phải có bộ đệm tĩnh. Tôi chọn triển khai lớp này dưới dạng một lớp đơn để tôi có thể viết lớp chỉ sử dụng các thành viên dữ liệu bình thường (không tĩnh). Điều này cho phép tôi vẫn tạo một định danh của lớp nếu tôi cần vì lý do nào đó (tuần tự hóa, thử nghiệm đơn vị, v.v.)


0

Singleton giống như một dịch vụ, như đã được đề cập. Pro là tính linh hoạt của nó. Tĩnh, tốt, bạn cần một số phần tĩnh để triển khai Singleton.

Singleton có mã để xử lý việc khởi tạo đối tượng thực, có thể là một trợ giúp tuyệt vời nếu bạn gặp phải các vấn đề về đua xe. Trong một giải pháp tĩnh, bạn có thể cần phải đối phó với các vấn đề đua xe tại nhiều vị trí mã.

Tuy nhiên, giống như Singleton có thể được xây dựng với một số biến tĩnh, bạn có thể so sánh nó với 'goto'. Nó có thể rất hữu ích cho việc xây dựng các cấu trúc khác, nhưng bạn thực sự cần biết cách sử dụng nó và không nên 'lạm dụng' nó. Do đó, khuyến nghị chung là hãy sử dụng Singleton và sử dụng static nếu bạn phải.

cũng kiểm tra bài đăng khác: Tại sao chọn một lớp tĩnh thay vì triển khai singleton?


0

tham khảo cái này

tóm lược:

a. Một nguyên tắc nhỏ mà bạn có thể làm theo là nếu nó không cần duy trì trạng thái, bạn có thể sử dụng một lớp Tĩnh, nếu không, bạn nên sử dụng một Singleton.

b. sử dụng Singleton nếu nó là một đối tượng đặc biệt "nặng". Nếu đối tượng của bạn lớn và chiếm một lượng bộ nhớ hợp lý, nhiều cuộc gọi n / w (nhóm kết nối) .. vv. Để đảm bảo rằng nó sẽ không được khởi tạo nhiều lần. Một lớp Singleton sẽ giúp ngăn chặn trường hợp như vậy từng xảy ra


-1

Khi lớp đơn cần trạng thái. Singletons duy trì trạng thái toàn cục, các lớp tĩnh thì không.

Ví dụ: tạo người trợ giúp xung quanh một lớp đăng ký: Nếu bạn có tổ hợp có thể thay đổi (Người dùng hiện tại HKey so với Máy cục bộ HKEY), bạn có thể thực hiện:

RegistryEditor editor = RegistryEditor.GetInstance();
editor.Hive = LocalMachine

Bây giờ bất kỳ lệnh gọi nào khác tới singleton đó sẽ hoạt động trên tổ máy Local Machine. Nếu không, bằng cách sử dụng một lớp tĩnh, bạn sẽ phải chỉ định mọi thông tin của Local Machine đó, hoặc có một phương thức như thế nào ReadSubkeyFromLocalMachine.


1
Tôi sẽ phản đối việc sử dụng Singleton theo cách này ... Rất nguy hiểm nếu để trạng thái kéo dài, bởi vì người gọi tiếp theo có thể quên thiết lập tổ ong và ghi vào sổ đăng ký sai tổ chức ... :-( Đây là có thể là một ví dụ về lạm dụng mà làm cho mọi người nghĩ Singletons là xấu.
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.