Singleton可以说是《Design Pattern》中最简单也最实用的一个设计模式。那么,什么是Singleton?
顾名思义,Singleton就是确保一个类只有唯一的一个实例。Singleton主要用于对象的创建,这意味着,如果某个类采用了Singleton模式,则在这个类被创建后,它将有且仅有一个实例可供访问。很多时候我们都会需要Singleton模式,最常见的比如我们希望整个应用程序中只有一个连接数据库的Connection实例;又比如要求一个应用程序中只存在某个用户数据结构的唯一实例。我们都可以通过应用Singleton模式达到目的。
一眼看去,Singleton似乎有些像全局对象。但是实际上,并不能用全局对象代替Singleton模式,这是因为:其一,大量使用全局对象会使得程序质量降低,而且有些编程语言例如C#,根本就不支持全局变量。其二,全局对象的方法并不能阻止人们将一个类实例化多次:除了类的全局实例外,开发人员仍然可以通过类的构造函数创建类的多个局部实例。而Singleton模式则通过从根本上控制类的创建,将"保证只有一个实例"这个任务交给了类本身,开发人员不可能再有其它途径得到类的多个实例。这一点是全局对象方法与Singleton模式的根本区别。
Singleton模式的实现
Singleton模式的实现基于两个要点:
1)不直接用类的构造函数,而另外提供一个Public的静态方法来构造类的实例。通常这个方法取名为Instance。Public保证了它的全局可见性,静态方法保证了不会创建出多余的实例。
2)将类的构造函数设为Private,即将构造函数"隐藏"起来,任何企图使用构造函数创建实例的方法都将报错。这样就阻止了开发人员绕过上面的Instance方法直接创建类的实例。
通过以上两点就可以完全控制类的创建:无论有多少地方需要用到这个类,它们访问的都是类的唯一生成的那个实例。以下C#代码展现了两种实现Singleton模式的方式,开发人员可以根据喜好任选其一。
实现方式一:Singleton.cs
using System; class SingletonDemo { private static SingletonDemo theSingleton = null; private SingletonDemo() {} public static SingletonDemo Instance() { if (null == theSingleton) { theSingleton = new SingletonDemo(); } return theSingleton; } static void Main(string[] args) { SingletonDemo s1 = SingletonDemo.Instance(); SingletonDemo s2 = SingletonDemo.Instance(); if (s1.Equals(s2)) { Console.WriteLine("see, only one instance!"); } } }
与之等价的另外一种实现方式是:Singleton.cs:
using System; class SingletonDemo { private static SingletonDemo theSingleton = new SingletonDemo(); private SingletonDemo() {} public static SingletonDemo Instance() { return theSingleton; } static void Main(string[] args) { SingletonDemo s1 = SingletonDemo.Instance(); SingletonDemo s2 = SingletonDemo.Instance(); if (s1.Equals(s2)) { Console.WriteLine("see, only one instance!"); } } }
编译执行:
Csc Singleton.cs
得到运行结果:
see, only one instance!
.NET中的Singleton
因为Singleton模式具有这样实用的价值,开发人员除了可以在程序代码中直接使用Singleton模式外,在许多大型系统的实现上也都处处可见它的影子。在微软隆重推出的.NET框架中,同样也可以发现Singleton思想闪烁的光芒。
举例来说,在.NET框架的重要组成部分Remoting中,远程对象(Remote Object)有两种激活方式:服务器端激活方式和客户端激活方式。采用服务器端激活方式的对象又分为两种类型:Singleton对象和SingleCall对象。Singleton 对象是这样的对象:无论该对象有多少个客户端调用,它总是只有一个实例,由这个实例来处理所有的客户端请求。相反地,若将远程对象声明为 SingleCall,则系统会为每次客户端方法的调用创建一个新对象,即使这些方法调用来自同一个客户端,也即,对象只在方法调用持续期间存在,一旦方法调用结束,该对象就会被销毁。显而易见,这里的Singleton对象就是设计模式Singleton思想在.NET中的应用。
那么,如何在.NET的Remoting中利用Singleton?.NET提供了两种方式将一个远程对象注册为Singleton:直接调用RegisterWellKnownServiceType方法,在参数中指定对象类型为Singleton;或在配置文件web.config中设定远程对象的类型为Singleton。这两种方法的效果相同,所不同的是后一种方法显得更加方便,因为改变配置文件的内容后,不必重新编译应用程序。下列代码显示了如何使用RegisterWellKnownServiceType方法注册远程对象类型:
RemotingConfiguration.RegisterWellKnownServiceType( Type.GetType("RemotingSamples.HelloServer,object"), "SayHello", WellKnownObjectMode.Singleton);
参数"SayHello"是客户端访问远程对象(这里是HelloServer)时用来代表远程对象的URI,例如tcp://localhost:8085/SayHello(假设使用的是TCP通道)。
最后一个参数就指明了这个远程对象是Singleton类型。一旦将远程对象注册为Singleton,则在第一次客户端调用HelloServer的方法时创建这个远程对象,然后保持它直到客户端中断连接或对象超时被销毁为止。在此期间,无论有多少个客户端调用这个远处对象,所有的客户请求都将由那个已经存在的唯一实例接受处理。
这就是Singleton在.NET中的应用。
从Singleton模式的实现和应用中也可以看出,优秀的设计模式往往都具有"简约之美"。它们采用一种"优雅"的方式,将那些成功的设计方法和体系结构能够得以被简单、方便地复用。这也是为什么现在的软件开发日益强调"设计模式"的原因之所在。如果想进一步了解更多的设计模式,还是推荐各位阅读Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides的经典之作《Design Pattern》。