前言:OO不能拒绝变化,而是要隔离变化。使得变化造成的影响在一个可以接受的范围内。
动机(Motivation):
在软件系统中经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例呢?这应该是类设计者的责任,而不是使用者的责任。我们不能让使用者保证只“New”一个对象……
意图(Intent)
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
例子:
public class Singleton
{
private static Singleton instance;
private Singleton(){}
public static Singleton Instance
{
get
{
if(instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
单线程Singleton模式的几个要点:
1)Singleton模式中的实例构造器可以设置为protected以允许子类派生。
2)Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。
3)Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷相违背。
4)Singleton模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理。
5)不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。
多线程Singleton模式实现
Class Singleton
{
private static volatile Singleton instance = null;
private static object lockHelper = new Object();
private Singleton(){}
public static Singleton Instance
{
get
{
if (instance ==null)
{
lock(lockHelper)
{
if(instance==null)
{
instance = new Singleton();
}
}
return instance;
}
}
}
}
//volatile关键字可以保证代码不被编译器微调。在多线程情况下如果删除此关键字,由于编译器对指令顺序的调整,还是可能出现多实例的情况。
另一种方式:
sealed class Singleton
{
public static readonly Singleton Instance = new Singleton();
private Singleton(){}
}
该方式虽然简单,但也实现了Singleton模式。静态构造器只在静态字段初始化之前进行调用,因此这个静态构造器会由.net framework自动保证只会被一个线程调用。
这种方式的缺点在于不能传递参数。(静态构造器不支持参数。因此可向后延迟,提供一些方法或属性为对象设置正确的状态。
Singleton模式扩展
1)将一个实例扩展到N个实例,就是对象池的实现。
2)将new构造器的调用转移到其他类中,例如多个类协同工作环境中,某个局部环境只需要拥有某个类的一个实例。
3)理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。
.NET框架中的Singleton应用
1)Type对象就是Singleton模式的一个实现。系统保证每一个类不同实例调用GetType()获得的Type对象是相等的。这属于“局部领域的Singleton模式”。
2)HttpContext对象也是一个例子。
推荐参考书
1)《设计模式:可复用面向对象软件的基础》GoF。
2)《面向对象分析与设计》Grady Booch
3)《敏捷软件开发:原则、模式与实践》RobertC.Martin。
4)《重构:改善既有代码的设计》Martin Flower
5)《Refactoring to Patterns》Joshua Kerievsky。
李建忠老师的Q&A
________________________________________
Q:使用静态的计数器一样可以在单线程中实现只实例化一个对象的目的啊
A:这个应该是不能的,因为静态计数器的作用和if (instance == null) 是一样的,在多线程环境中都会有问题的。
________________________________________
Q:多线成中的lock可以lock(this)吗?
A:因为是在静态属性中,所以不能访问this指针。
________________________________________
Q:为什么双检查?
A:单检查也是可以的,但是单检查的效率要比双检查低——因为同步控制的时间太长了。双检查能够最高效地实现多线程安全的访问。
________________________________________
Q:为什么一定要加readonly关键字?
A:这个readonly关键字只是不希望客户程序将Instance字段设置为null等不合理的值。
________________________________________
Q:remoting里面的Singleton对象应该是使用了Singleton模式吧
A:是的,.NET Remoting中的服务器对象激活中就使用了Singleton模式
________________________________________
Q:怎样获得类已经构造的实例的个数?
A:可以在实例构造器中放一个静态的字段,来表示计数器——在实例构造器中每次做count++即可。
________________________________________
Q:怎样区分各个模式,学了很久,总是搞不清楚他们之间的区别,经常性的搞混
A:区分模式的最好办法是搞清楚为什么有这些模式,各个模式分别应对什么样的变化。
________________________________________
Q:当好一个程序员必须要学好设计模式吗?它在代码编写过程中有什么好处?怎样可以学好设计模式?
A:不一定,我了解的某些天才程序员对设计模式并不感兴趣——主要是因为他们首先不是面向对象程序员? 但是学好设计模式对于一个面向对象程序员有莫大帮助。学好设计模式的关键是深刻理解面向对象。
________________________________________
Q:lock 对于singleton本身的类使用 与 使用 helper有什么区别?
A:本质上没什么区别,但是别忘了这时候Singleton对象还没有创建? 所以这时候不可能lock一个Singleton对象。
________________________________________
Q:我有一个疑问,在singleton设计模式下,什么时候,由谁来创建这个实例呢?
A:Singleton模式中的“缓式加载”已经说明了Singleton的实例是在客户程序第一次调用GetInstance方法时才会被创建。
________________________________________
Q:我大致的翻过设计模式这本书,我想请教下您,您认为在设计一个很好的面向对象的软件与程序语言的选择(比如C#,C++,JAVA)二者之间怎么做到最好的搭配
A:我个人认为这三门语言都是很好的面向对象语言,都能很充分地发挥面向对象的力量。在面向对象层次上,它们的差别并不大。
________________________________________
Q:在多线程环境中,使用Static实例化一个对象后,那么它的实例的方法是否可以保证执行时不致冲突?
A:实例方法在多线程环境中无所谓冲突,关键是实例方法操作的实例数据——如果有的话——有可能冲突。