单件模式用来创造独一无二的,只有一个实例的对象入场券。在软件开发中,有时需要这样的特殊实例,比如一个线程池(threadpool)、一个缓存(cache)、一个日志对象、一个虚拟打印机等。单件模式就是针对上述问题而产生的,它保证一个类仅有一个实例,并提供一个它的全局访问点。
当然,保证仅有一个实例还有其他办法,比如程序员之间的约定或者是提供一个静态的全局变量。但是,这两个方法都是不安全的!约定不能保证人人都记得这个规则,特别对于分布式开发。而静态的全局变量,也不能阻止人们使用new来创建一个新对象。
所以,单件模式提供的类创建机制必须实现以下两个功能:(1)要把实例的产生从“类的使用者”手里拿过来,即在“类的创建者”中完成;(2)要禁止外部使用new关键字来创建类的实例;
2 分析实现
为了完成上面的目标,我们应该在类自身完成类的创建。此外,为了禁止外部使用new来实例化类,需要在类中声明一个私有构造函数,阻止外部声明。另外,再提供一个外部的访问点。代码很简单,如下:
{
// 用于返回的静态实例
private static Singleton m_Singleton;
// 私有构造函数,阻止外部使用new关键字创建这个类的新实例
private Singleton() { }
public static Singleton GetInstance()
{
if (m_Singleton == null )
{
m_Singleton = new Singleton();
}
return m_Singleton;
}
}
上面的代码很简单,就不再解释什么了。需要注意的是,上面使用了“晚加载(Lazy Load)”的技术,即在使用前才实例化需要的单件。
3 对Singleton的扩展
(1)多线程下的Singleton
普通的单件模式无法适应多线程状况下。很明显,在第一个线程判断实例是否为null后,如果阻塞了,那么很可能创建出两个或以上的Singleton实例。所以,代码需要改进:
{
private static volatile ThreadSingleton m_Singleton;
// 线程锁辅助器,没有其他用途,仅为了辅助锁定线程
private static object m_LockHelper = new object ();
private ThreadSingleton(){}
public static ThreadSingleton GetInstance()
{
// 双检查
if (m_Singleton == null )
{
lock (m_LockHelper)
{
if (m_Singleton == null )
{
m_Singleton = new ThreadSingleton();
}
}
}
return m_Singleton;
}
}
关键字volatile指定编译器按照代码顺序编译(有时,编译器出于优化的目的,会改变代码的执行顺序),这个关键字从严格意义上防止了线程可能的错误。m_LockHelper只是一个线程锁,不参与实际操作。其后使用了一个双检查,在第一次判断之后,锁定线程,然后再次判断。防止线程的错误。
(2)性能改善的Singleton
如果Singleton在线程下被频繁调用,那么,可能会造成程序性能的下降。下面的方法很简单:
{
public static readonly SimpleSingleton Instance = new SimpleSingleton();
private SimpleSingleton() { }
}
对于只读的静态变量,.NET的机制实际上实现了下面的代码(隐含实现):
{
Instance = new SimpleSingleton();
}
该代码表示两层意思:(1).NET框架限制了静态构造器仅在一个线程内运行,即实现了多线程;(2) .NET框架下的静态构造器,使用了晚加载效果,即仅在使用时才初始化。
(3)多实例的Singleton
Singleton模式只创建一个实例。那么,实际的隐含意思是可以创建指定个数的实例。对Singleton的真正理解是:Singleton可以创建指定个数的类的实例。既然可以生成一个,也就可以生成多个。实现多实例的Singleton,代码和前面的没有什么很大的区别。
{
private static MutiSingleton[] m_MutiSingleton = new MutiSingleton[ 10 ];
private MutiSingleton(){}
public static MutiSingleton GetInstance( int index)
{
if (m_MutiSingleton[index] == null )
{
m_MutiSingleton[index] = new MutiSingleton();
}
return m_MutiSingleton[index];
}
}
4 对Singleton的总结
Singleton模式是对全局变量的改进。它避免了那些存储唯一实例的全局变量污染名称空间。Singleton也可以派生扩展,但是,因为构造器是私有的,所以不能通过这个来扩展类。此外,受限于私有的变量引用,它会和子类共享这个静态变量。所以,要想让子类工作顺利,需要借助注册表功能,让它来保存字符串和单件之间的映射。但是,单件模式在整个设计模式中不是重要的模式,应用也不多。所以,如果发现需要扩展派生Singleton,首先应该是检查代码,看看究竟需不需要这么做。
一句话归纳:Singleton模式适用于系统仅需要一个对象实例的特殊情况。类保有一个自身的静态变量,并隐藏构造函数。通过一个静态方法向外界公开自身的静态变量。考虑性能,可以使用延迟加载技术。