什么时候应该使用单例模式而不是静态类?

在决定使用单例还是静态类时,命名devise注意事项。 在这样做的时候,你被迫把两者进行对比,所以无论你想出什么样的对比,都可以用来展示你的思维过程! 另外,每个采访者都喜欢看例子。 🙂

  • 单例可以实现接口并从其他类inheritance。
  • 单身可以被延迟加载。 只有在实际需要的时候。 如果初始化包含昂贵的资源加载或数据库连接,那非常方便。
  • 单身人士提供了一个实际的对象。
  • 单身人士可以延伸到工厂。 幕后的对象pipe理是抽象的,所以更好的维护性和更好的代码。

如何“避免两个”? 单身人士和静态类:

  • 可能会引入全球状态
  • 与其他多个类紧密结合
  • 隐藏依赖关系
  • 可以使unit testing课程孤立困难

相反,看看dependency injection和控制容器库的反转 。 几个IoC库将为您处理生命周期pipe理。

(像往常一样,有一些例外,如静态math类和C#扩展方法。)

我认为唯一的区别是语法:MySingleton.Current.Whatever()vs MySingleton.Whatever()。 正如大卫所说,这个国家在这两种情况下最终都是“静止的”。


编辑:埋葬旅来自digg …无论如何,我想到一个案件,将需要一个单身人士。 静态类不能从基类inheritance,也不能实现一个接口(至less在.Net中是不能的)。 所以,如果你需要这个function,那么你必须使用一个单身人士。

关于这个问题我最喜欢的讨论之一是在这里 (原来的网站下,现在链接到互联网归档Wayback机器 。)

总结一下Singleton的灵活性优势:

  • 一个单身人士可以很容易地转换成一个工厂
  • 一个单身人士可以很容易地修改返回不同的子类
  • 这可以导致更可维护的应用程序

有一个静态variables的静态类是一个黑客的位。

 /** * 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; } } 

具有实际实例的单身人士更好。

 /** * 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; } } 

国家只在这个例子中。

因此,单身人士可以稍后进行修改以进行合并,线程本地实例等等。而且已经编写的代码中没有一个需要改变以获得好处。

 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; } 

有可能做类似于静态类的事情,但是在间接调度中会有每次调用的开销。

您可以获取实例并将其作为parameter passing给函数。 这让代码被定向到“正确的”单例。 我们知道你只需要其中的一个…直到你不需要。

最大的好处是有状态的单例可以做成线程安全的,而静态类不能,除非你把它修改成一个秘密的单例。

想象一个像服务一样的单身人士。 这是一个提供了一组特定function的对象。 例如

 ObjectFactory.getInstance().makeObject(); 

对象工厂是执行特定服务的对象。

相比之下,一个充满静态方法的类是您可能想要执行的一组操作的集合,它被组织在一个相关的组(类)中。 例如

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

这里的StringUtils示例是可以在任何地方应用的function集合。 单体工厂对象是具有明确责任的特定types的对象,可以在需要时创build和传递。

静态类在运行时被实例化。 这可能是耗时的。 单身人士只能在需要的时候实例化。

不应该像静态类一样使用单例。 在本质上,

 MyStaticClass.GetInstance().DoSomething(); 

基本上是一样的

 MyStaticClass.DoSomething(); 

你应该做的是把单身人士视为另一个对象 。 如果服务需要单例types的实例,则在构造函数中传递该实例:

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

该服务不应该意识到该对象是一个单身人士,并应该把对象作为一个对象。

这个对象当然可以作为一个实现的细节,也可以作为整体configuration的一个方面来实现,如果这样做更容易的话,可以把它作为一个单例实现 但是使用对象的东西不应该知道对象是否是单例。

如果通过“静态类”你是指只有静态variables的类,那么它们实际上可以维持状态。 我的理解是,唯一的区别就是你如何访问这个东西。 例如:

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

 MySingleton.doSomething(); 

MySingleton的内部在它们之间显然是不同的,但除了线程安全问题之外,它们对于客户端代码来说都是一样的。

单例模式通常用于为多个线程同时访问数据的实例独立或静态数据提供服务。 一个例子可以是国家代码。

应该永远不要使用单身(除非你考虑一个没有可变状态的单身人士)。 “静态类”应该没有可变状态,除了线程安全的caching之外。

几乎任何一个单例的例子都说明了怎么做不到。

如果一个单件是你可以处理的东西,在它之后进行清理,那么当它是一个有限的资源(也就是只有一个)时,你可以考虑它,你不需要所有的时间,并且有一些记忆或分配时的资源成本。

清理代码看起来更自然,当你有一个单身人士,而不是静态类包含静态字段。

但是,代码看起来也是一样的,所以如果你有更具体的要求,也许你应该详细说明。

这两者可能非常相似,但请记住,真正的单身人士必须自己实例化(授予,一次),然后服务。 返回mysqli实例的PHP数据库类实际上并不是一个Singleton(正如一些人所说的那样),因为它返回的是另一个类的实例,而不是实例作为静态成员的类的实例。

所以,如果你正在编写一个新的类,你打算在你的代码中只允许一个实例,那么你可以把它写成一个Singleton。 把它看作是写一个简单的类,然后join它来促进单实例化的要求。 如果你使用别人的类,你不能修改(如mysqli ),你应该使用一个静态类(即使你没有在关键字前定义它的定义)。

单例更灵活,在希望Instance方法根据某个上下文返回Singletontypes的不同具体子类的情况下可以使用单例。

静态类不能作为parameter passing; 单身人士的情况可以。 正如其他答案中提到的,注意静态类的线程问题。

RP

单身人士可能有一个构造函数和析构函数。 根据你的语言,构造函数可能会在第一次使用你的单例时被自动调用,或者如果你的单例根本不被使用,那么永远不会。 静态类不会有这样的自动初始化。

一旦获得对单例对象的引用,就可以像使用其他对象一样使用它。 如果之前存储对单例的引用,客户端代码可能甚至不需要知道它使用单例:

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

当你select抛弃Singleton模式而使用IoC这样的真实模式时,这显然会让事情变得简单。

如果需要在运行时计算某些东西(如查找表),则可以使用单例模式。

我认为一个比单纯的静态类更有意义的地方就是当你必须构build一个昂贵的资源池时(比如数据库连接)。 如果没有人使用它们,您将不会对创build池感兴趣(静态类将意味着您在加载类时执行成本高昂的工作)。

如果你想强制高效caching数据,单身也是一个好主意。 例如,我有一个类在xml文档中查找定义。 由于parsing文档可能需要一段时间,因此我设置了一个定义caching(我使用SoftReferences来避免outOfmemeoryErrors)。 如果所需的定义不在caching中,则执行昂贵的xmlparsing。 否则,我从caching中返回一个副本。 由于有多个caching意味着我仍然可能不得不多次加载相同的定义,我需要一个静态caching。 我select将这个类作为一个单例实现,这样我就可以只使用普通(非静态)数据成员来编写这个类。 这使我仍然可以创build一个类,如果我需要它由于某种原因(序列化,unit testing等)

如前所述,辛格尔顿就像一种服务。 Pro是它的灵活性。 静态的,那么你需要一些静态部分来实现Singleton。

辛格尔顿有代码来处理实际对象的实例,如果遇到赛车问题,这可能是一个很大的帮助。 在一个静态解决scheme中,您可能需要处理多个代码位置的赛车问题。

但是,与Singleton相同,可以用一些静态variables来构build,你可以把它和goto进行比较。 build设其他结构可能是非常有用的,但是你真的需要知道如何使用它,而不是“过度使用”它。 因此,一般build议是坚持单身,如果必须的话,使用静态。

还检查其他post: 为什么select一个静态类超过一个单一的实现?

参考这个

概要:

一个。 一个简单的经验法则是,如果不需要维护状态,可以使用Static类,否则应该使用Singleton。

湾 使用单例就是它是一个特别“重”的对象。 如果你的对象很大,占用了一定量的内存,那么很多n / w调用(连接池)等等。 确保它不会被多次实例化。 一个Singleton类将有助于防止这种情况发生

当单个类需要状态时。 单身人士保持全球状态,静态类别不。

例如,围绕registry类做一个帮手:如果你有可变的configuration单元(HKey当前用户与HKEY本地机器),你可以去:

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

现在,任何进一步的单身通话将在本地机器上运行。 否则,使用静态类,您将不得不指定本地计算机configuration单元everytiem,或者有像ReadSubkeyFromLocalMachine这样的方法。