解读设计模式----单例模式(Singleton Pattern)
单例模式可以保证一个类有且只有一个实例,并提供一个访问它的全局访问点.在程序设计中,有很多情况需要确保一个类只能有一个实例.从这句话可以看出,Singleton模式的核心:如何控制用户使用new对一个类的实例构造器的任意调用。如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?这应该是类设计者的责任,而不是使用者的责任。
一、单例模式意图
保证一个类有且只有一个实例,并提供一个访问它的全局访问点。
二、单例模式UML图(该图来至http://www.dofactory.com/)
三、示例解说单例模式
看看下面这个简单的示例:
2{
3 public class Singleton
4 {
5 //静态私有属性
6 private static Singleton instance;
7
8 /// <summary>
9 /// 私有构造器--让类的使用者调用不到此构造器
10 /// </summary>
11 private Singleton()
12 { }
13
14 public static Singleton Instance
15 {
16 get
17 {
18 if (instance == null)
19 {
20 instance = new Singleton();
21 }
22 return instance; //返回的总是第一次实例的对象
23 }
24 }
25 }
26
27 //测试类
28 class TestSingleton
29 {
30 public static void Main2(string[] args)
31 {
32 Singleton t1 = Singleton.Instance;
33 Singleton t2 = Singleton.Instance;
34 Console.WriteLine(object.ReferenceEquals(t1, t2) == true);
35 }
36 }
37}
提供一个静态的私有属性,并提供get来实现一个简单的单例.此外我们还可以通过静态只读属性来实现.看看下面这个MSDN上提供的示例:
2/// MSDN上Singleton模式的实现
3/// </summary>
4public class MsdnSingleton
5{
6 //声明的同时进行初始化
7 public static readonly MsdnSingleton Instance = new MsdnSingleton();
8
9 /// <summary>
10 /// 私有构造器
11 /// </summary>
12 private MsdnSingleton()
13 {
14 }
15}
这样的单例实现的实质等同于提供一个静态的属性字段,通过静态构造器来初始化这个属性.因为要想访问静态字段,那静态构造器就首先执行,下面是代码示例:
2{
3 //要想访问静态字段,那静态构造器就首先执行
4 public static readonly SameSingleton Instance;
5
6 /// <summary>
7 /// 静态构造器-初始化Instance
8 /// 静态构造器-完成静态字段(Instance)的初始化
9 /// </summary>
10 static SameSingleton()
11 {
12 Instance = new SameSingleton();
13 }
14
15 /// <summary>
16 /// 私有构造器
17 /// </summary
18 private SameSingleton()
19 {
20
21 }
22}
上面的实现完全可以达到单例模式的意图,保证一个类仅且有一个实例,并提供一个全局访问点.而这在实际应用我们还得注意一点,就是在多线程的调用.于此,我们还得保证线程安全.要保证线程安全其实也是很简单就可以实现的,只需要通过加锁和双重判断就OK,下面是简单的多线程的线程安全的实现:
2using System.Collections.Generic;
3using System.Text;
4
5//多线程的Singleton模式实现
6namespace DesignPattern.Singleton
7{
8 public class SingletonMultithread
9 {
10 private static object lockHelper = new object();
11
12 private static volatile SingletonMultithread instane = null;
13 public static SingletonMultithread Instane
14 {
15 get
16 {
17 if (instane == null) //双检查
18 {
19 lock (lockHelper)
20 {
21 if (instane == null)
22 {
23 instane = new SingletonMultithread();
24 }
25 }
26 }
27 return instane;
28 }
29 }
30
31
32 //私有构造方法
33 private SingletonMultithread()
34 {
35
36 }
37 }
38
39 class TestSingletonMultithread
40 {
41 public static void Main()
42 {
43 SingletonMultithread s1 = SingletonMultithread.Instane;
44 SingletonMultithread s2 = SingletonMultithread.Instane;
45 Console.WriteLine(object.ReferenceEquals(s1, s2) == true);
46 }
47 }
48}
49
其中:private static volatile Singleton instance=null;
--volatile:保证特定平台的实现必须不要去从新调整指令,保证对象构造的一个非常严格的顺序。
四、真实项目中的单例
我曾经做过一个网球场的管理系统,客户要求使用C/S,(随便PS下客户,在我做需求的时候建议客户做B/S系统,管理和部署就很方便,也不用带上几百M的.NET Frameworks去装在客户电脑上了。可好心讨不了客户的笑颜,他硬说我是在给他做网页,还说他要的是软件不是网页,汗过........),在使用C/S做程序的时候我想大家都遇到过这样的情况,无论是通过按扭点击还是菜单来导航,通常我们是像下面这样实现的:
2{
3 ratForm rat = new ratForm();
4 rat.MdiParent = this;
5 rat.Show();
6
7}
我这里是使用的菜单控件,通过点击打开新窗体。如果是这样问题就出现了,每当我们点击一次菜单上的菜单项就会启动一个窗体,点几次就会启动几个窗体。见下图:
这样显然是不符合要求的,我们应该做到让每个窗体只能Show一个出来,这时 单例模式 就能派上用场了。要保证外部不能创建类的实例,才单例中就是通过设置构造方法为私有private,所谓模式可说是一种设计套路,这里我们就依葫芦画瓢来画一次单例,设置构造方法私有,代码如下;
2{
3 InitializeComponent();
4}
通过一个静态方法来对完成静态属性的初始化;
2public static ratForm GetInstance()
3{
4 if (ratf == null || ratf.IsDisposed)
5 {
6 ratf = new ratForm();
7 ratf.MdiParent = mainForm.ActiveForm;
8 }
9 return ratf;
10}
通过单例模式的引入,私有了构造方法,并通过一个静态的方法初始化静态字段,保证了类的唯一实例,方法GetInstance()就是单例的全局访问点。这样设计后就通过GetInstance()方法就得了这个窗体的唯一实例。
2{
3 //ratForm rat = new ratForm();
4 //rat.MdiParent = this;
5 //rat.Show();
6 ratForm.GetInstance().Show();
7}
通过单例模式的引入,改善了程序的设计,在窗体调用处只需要通过全局访问点这个静态方法就可以得到唯一的实例对象,然后调用其Show()方法,就达到了我们的要求。
五、使用单线程Singleton模式要点
--Singleton模式中的实例构造器可以设置为protected以也许子类派生。
--Singleton模式一般不要支持ICloneable接口,因为这可能导致对个对象实例,与Singleton的意图违背。
--Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的意图违背。
--Singleton模式只考虑到了对象创建的管理,没有考虑对销毁的管理。对于自带垃圾回收的平台可不考虑这点。
上面总结源于MSDN WebCast
---------------------------------------------------------------------------------------------------------