设计模式之singleton
——————————————————————————–
引言
相信大多数拜读过"gang of four"(erich gamma, richard helm, ralph johnson, john vlissides)的经典之作《design pattern》的同僚们,对这本书一定推崇有加。曾有人这么宣告:"只有在读过《design pattern》后,我的编程水平才真正得到了质的飞跃。"
那么,如何才能步入设计模式的殿堂?设计模式是资深程序员日积月累总结出来的一套可复用的、针对面向对象软件设计的解决方案,从这个意义上说,世界上存在无数多的设计模式,"gang of four"总结的23种设计模式只是其中的23个精华。入手的关键就在于领会"设计模式"的思想,然后再将它们融会贯通、灵活应用到自己到开发过程中。
——————————————————————————–
singleton模式
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》。
——————————————————————————–
作者:刘英丹
