什么时候应该使用单例模式而不是静态类?
在决定使用单例还是静态类时,请列出设计注意事项。在执行此操作时,我们被迫将两者进行对比,因此可以提出的任何对比也都有助于显示思维过程!此外,每个面试官都喜欢看示例性例子。 :)
解决方案
回答
当单个类需要状态时。单例保持全局状态,静态类不保持全局状态。
例如,围绕一个注册表类创建一个助手:如果我们有可更改的配置单元(HKey当前用户与HKEY本地计算机),则可以执行以下操作:
RegistryEditor editor = RegistryEditor.GetInstance(); editor.Hive = LocalMachine
现在,对该单例的任何进一步调用都将在"本地计算机"配置单元上进行。否则,使用静态类,则必须指定"本地计算机"配置单元,或者使用类似" ReadSubkeyFromLocalMachine"的方法。
回答
我认为唯一的区别是语法:MySingleton.Current.Whatever()与MySingleton.Whatever()。正如大卫提到的那样,无论哪种情况,国家最终都是"静态的"。
编辑:埋葬大队是从digg过来的...无论如何,我想到了一个需要单例的案件。静态类不能从基类继承,也不能实现接口(至少在.Net中它们不能)。因此,如果需要此功能,则必须使用单例。
回答
如果用"静态类"表示仅具有静态变量的类,则它们实际上可以维护状态。我的理解是,唯一的区别就是我们访问该内容的方式。例如:
MySingleton().getInstance().doSomething();
相对
MySingleton.doSomething();
MySingleton的内部结构之间显然会有所不同,但是,除了线程安全性问题,它们在客户端代码方面都将执行相同的操作。
回答
永远不要使用单例(除非我们将没有可变状态的类视为单例)。 "静态类"应该没有可变状态,除了线程安全的高速缓存之类。
几乎任何一个单例示例都说明了如何不这样做。
回答
如果单身人士是我们可以处理的东西,可以对其进行清理,如果它是有限的资源(即仅其中之一),我们不需要一直使用它并拥有某种内存,则可以考虑将其清理掉。分配时的资源成本。
与具有静态状态字段的静态类相反,当我们具有单例时,清理代码看起来更自然。
但是,无论哪种方式,该代码看起来都是相同的,因此,如果我们有更具体的询问理由,也许应该详细说明一下。
回答
两者可能非常相似,但请记住,真正的Singleton必须本身被实例化(授予一次)然后再提供。一个返回" mysqli"实例的PHP数据库类实际上并不是一个单例(正如某些人所说的),因为它返回的是另一个类的实例,而不是将该实例作为静态成员的类的实例。
因此,如果我们要编写一个新类,并计划只在代码中允许一个实例,则最好将其编写为Singleton。可以将其视为编写纯简类并添加到其中以方便单实例化要求。如果我们使用的是无法修改的其他人的类(例如mysqli),则应该使用静态类(即使我们未能在其定义前添加关键字)。
回答
Singleton更灵活,在我们希望Instance方法根据某些上下文返回Singleton类型的不同具体子类的情况下,这很有用。
回答
静态类不能作为参数传递。单例实例即可。如其他答案中所述,请注意静态类的线程问题。
rp
回答
单例可能具有构造函数和析构函数。根据语言,第一次使用单例时,可能会自动调用该构造函数;如果根本不使用单例,则永远不会调用该构造函数。静态类将没有此类自动初始化。
一旦获得了对单例对象的引用,就可以像使用任何其他对象一样使用它。如果对单例的引用存储在以下位置,则客户端代码甚至可能不需要使用单例来了解其代码:
Foo foo = Foo.getInstance(); doSomeWork(foo); // doSomeWork wont even know Foo is a singleton
当我们选择抛弃Singleton模式以使用IoC之类的真实模式时,这显然使事情变得容易。
回答
当我们需要在运行时计算某些内容时,请使用单例模式,如果可以的话,这些内容应在编译时进行计算,例如查找表。
回答
将单例视为服务。它是提供一组特定功能的对象。例如。
ObjectFactory.getInstance().makeObject();
对象工厂是执行特定服务的对象。
相比之下,充满静态方法的类是我们可能想要执行的操作的集合,并以相关组(The类)的形式组织。例如。
StringUtils.reverseString("Hello"); StringUtils.concat("Hello", "World");
这里的StringUtils示例是可以在任何地方应用的功能的集合。单例工厂对象是具有明确职责的特定类型的对象,可以在需要时创建和传递该对象。
回答
关于此问题,我最喜欢的讨论之一是在此(原始站点关闭,现在链接到Internet Archive Wayback Machine。)
总结Singleton的灵活性优势:
- 单例可以轻松地转换为工厂
- 可以轻松修改Singleton以返回不同的子类
- 这样可以使应用程序更易于维护
回答
静态类在运行时实例化。这可能很耗时。仅在需要时才能实例化单例。
回答
我认为,当我们必须构造昂贵的资源池(例如数据库连接)时,Singleton比静态类更有意义。如果没有人使用过池,那么我们对创建池就不会有兴趣(静态类将意味着我们在加载类时会进行昂贵的工作)。
回答
带有大量静态变量的静态类有点麻烦。
/** * 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; }
可以对静态类执行类似的操作,但是在间接调度中会存在每次调用的开销。
我们可以获取实例并将其作为参数传递给函数。这样就可以将代码定向到"正确的"单例。我们知道我们只需要其中之一...直到我们不需要。
最大的好处是可以使有状态的单例成为线程安全的,而静态类则不能,除非我们将其修改为秘密的单例。
回答
- 单例可以实现接口并从其他类继承。
- 单例可以被延迟加载。仅在实际需要时。如果初始化包括昂贵的资源加载或者数据库连接,那将非常方便。
- 单例提供一个实际的对象。
- 单例可以扩展到工厂。幕后的对象管理是抽象的,因此可以更好地维护并产生更好的代码。
回答
单例不应该以与静态类相同的方式使用。本质上,
MyStaticClass.GetInstance().DoSomething();
基本上与
MyStaticClass.DoSomething();
我们实际上应该做的是将单例视为另一个对象。如果服务需要单例类型的实例,则在构造函数中传递该实例:
var svc = new MyComplexServce(MyStaticClass.GetInstance());
服务不应意识到该对象是单例对象,应将该对象视为一个对象。
当然,可以将对象实现为实现细节和整体配置的一个方面,如果可以简化的话,可以实现为单例。但是使用该对象的事物不必知道该对象是否为单例。
回答
如果我们要强制高效缓存数据,则单例也是一个好主意。例如,我有一个在xml文档中查找定义的类。由于解析文档可能需要一段时间,因此我设置了一个定义缓存(我使用SoftReferences来避免outOfmemeoryErrors)。如果所需的定义不在高速缓存中,则执行昂贵的xml解析。否则,我将从缓存中返回一个副本。由于具有多个缓存将意味着我仍然可能必须多次加载相同的定义,因此我需要具有静态缓存。我选择将该类实现为单例,以便仅使用常规(非静态)数据成员来编写该类。这使我仍然可以出于某种原因(序列化,单元测试等)需要,创建类的实例化。
回答
"避免两者"怎么样?单例和静态类:
- 可能会引入全球状态
- 与其他多个班级紧密联系
- 隐藏依赖项
- 可能会使孤立地进行单元测试的课程变得困难
相反,请查看"依赖注入"和"控制容器库的倒置"。一些IoC库将为我们处理生命周期管理。
(和往常一样,也有例外,例如静态数学类和Cextension方法。)
回答
单例模式通常用于为实例独立或者静态数据提供服务,其中多个线程可以同时访问数据。一个示例可以是状态代码。