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