C#设计模式:单件(例)模式 -- 类也玩计划生育

这里写的代码,相当于《Head First 设计模式》的读书笔记,原书是java的,自己在学习的过程中将其翻译为C#:

(一)剖析经典的单件模式实现

  单件模式

  -- 确保一个类只有一个实例,并提供一个全局访问点

  -- 单件模式的类图可以说是所有模式的类图中最简单的

  -- 有一些对象其实我们只需一个,如线程池、缓存、对话框、处理偏好设置和注册表的对象、日志对象和充当打印机、显卡等设备的驱动程序的对象等。如果制造出多个实例,可能导致许多问题,如程序的行为异常、资源使用过度,或者结果不一致等

 

  1.新建一个控制台应用程序:SingletonPatternDemo。

  2.新建一个类:Singleton.cs

 1 namespace SingletonPatternDemo
 2 {
 3     public class Singleton
 4     {
 5         /// <summary>
 6         /// 利用一个静态变量来记录Singleton类的唯一实例
 7         /// </summary>
 8         private static Singleton _uniqueInstance;
 9 
10         //这里是其它变量...
11 
12         /// <summary>
13         /// 构造器私有化:只能在类内部才能调用构造器
14         /// </summary>
15         private Singleton() { }
16 
17         /// <summary>
18         /// 只能通过该方法获取到对象实例
19         /// </summary>
20         /// <returns></returns>
21         public static Singleton GetInstance()
22         {
23             //【注意】如果我们不需要该实例,它就永远不会产生。这就是“延迟实例化”(lazy instantiaze)
24             return _uniqueInstance ?? (_uniqueInstance = new Singleton());
25 
26             #region 上行相当于以下代码
27             //if (_uniqueInstance == null)
28             //{
29             //    _uniqueInstance = new Singleton();
30             //}
31 
32             //return _uniqueInstance;
33             #endregion
34         }
35 
36         //这里是其它方法...
37     }
38 }

  下面我们去掉注释看看

 1 namespace SingletonPatternDemo
 2 {
 3     public class Singleton
 4     {
 5         private static Singleton _uniqueInstance;
 6 
 7         private Singleton() { }
 8 
 9         public static Singleton GetInstance()
10         {
11             return _uniqueInstance ?? (_uniqueInstance = new Singleton());
12         }
13     }
14 }

  哇塞,这么简单啊!如果你也这么认为的话,那就错啦......接下来,我们看下第(二)部分

(二)场景应用

  巧克力工厂

  现代化的巧克力工厂具备计算机控制的巧克力锅炉,锅炉做的事,就是把巧克力和牛奶融在一起,然后送到下一个阶段,以制造成巧克力棒。

  这里有一个Choc-O-Holic公司的工业强度巧克力锅炉控制器,用于控制锅炉的日常运作,比如:锅炉内为空时才可以加入原料、锅炉内存在原料并且尚未煮沸时才能够进行煮沸,还有排出牛奶和巧克力的混合物时要求炉内存在已经煮沸的原料等。

  下列是巧克力锅炉控制器的代码:

 1 namespace SingletonPatternDemo
 2 {
 3     /// <summary>
 4     /// 巧克力锅炉
 5     /// </summary>
 6     public class ChocolateBoiler
 7     {
 8         private bool Empty { get; set; }
 9         private bool Boiled { get; set; }
10 
11         //代码开始时,锅炉为空,未燃烧
12         public ChocolateBoiler()
13         {
14             Empty = true;
15             Boiled = false;
16         }
17 
18         /// <summary>
19         /// 填充
20         /// </summary>
21         public void Fill()
22         {
23             //在锅炉内填入原料时,锅炉必须为空;
24             //填入原料后就把两个属性标识好
25             if (Empty)
26             {                
27                 //在锅炉内填满巧克力和牛奶的混合物...
28 
29                 Empty = false;
30                 Boiled = false;
31             }
32         }
33 
34         /// <summary>
35         /// 排出
36         /// </summary>
37         public void Drain()
38         {
39             //锅炉排出时,必须是满的,并且是煮过的;
40             //排出完毕后将Empty标志为true。
41             if (!Empty && Boiled)
42             {
43                 //排出煮沸的巧克力和牛奶...
44 
45                 Empty = true;
46             }
47         }
48 
49         /// <summary>
50         /// 煮沸
51         /// </summary>
52         public void Boil()
53         {
54             //煮混合物时,锅炉必须是满的,并且是没有煮过的;
55             //煮沸后,就把Boiled标识为true。
56             if (!Empty && !Boiled)
57             {
58                 //将炉内物煮沸...
59 
60                 Boiled = true;
61             }
62         }
63     }
64 }

试试根据(一)中所学的内容将它修改成单例模式

 1 namespace SingletonPatternDemo
 2 {
 3     /// <summary>
 4     /// 巧克力锅炉
 5     /// </summary>
 6     public class ChocolateBoiler
 7     {
 8         private static ChocolateBoiler _uniqueInstance; //【新增】一个静态变量
 9 
10         private bool Empty { get; set; }
11         private bool Boiled { get; set; }
12 
13         //代码开始时,锅炉为空,未燃烧
14         private ChocolateBoiler()   //【修改】原来是public
15         {
16             Empty = true;
17             Boiled = false;
18         }
19 
20         /// <summary>
21         /// 获取ChocolateBoiler对象实例
22         /// </summary>
23         /// <returns></returns>
24         public static ChocolateBoiler GetInstance() //【新增】一个静态方法
25         {
26             return _uniqueInstance ?? (_uniqueInstance = new ChocolateBoiler());
27         }
28 
29         /// <summary>
30         /// 填充
31         /// </summary>
32         public void Fill()
33         {
34             //在锅炉内填入原料时,锅炉必须为空;
35             //填入原料后就把两个属性标识好
36             if (Empty)
37             {                
38                 //在锅炉内填满巧克力和牛奶的混合物...
39 
40                 Empty = false;
41                 Boiled = false;
42             }
43         }
44 
45         /// <summary>
46         /// 排出
47         /// </summary>
48         public void Drain()
49         {
50             //锅炉排出时,必须是满的,并且是煮过的;
51             //排出完毕后将Empty标志为true。
52             if (!Empty && Boiled)
53             {
54                 //排出煮沸的巧克力和牛奶...
55 
56                 Empty = true;
57             }
58         }
59 
60         /// <summary>
61         /// 煮沸
62         /// </summary>
63         public void Boil()
64         {
65             //煮混合物时,锅炉必须是满的,并且是没有煮过的;
66             //煮沸后,就把Boiled标识为true。
67             if (!Empty && !Boiled)
68             {
69                 //将炉内物煮沸...
70 
71                 Boiled = true;
72             }
73         }
74     }
75 }
点击查看答案

 

【问题】万一同时存在多个ChocolateBoiler(巧克力锅炉),可能将发生很多糟糕的事情!... 敬请收看第(三)部分

 

(三)处理多线程

  现在,只要使用lock,就可以很简单地解决(二)中出现的问题了

 1 namespace SingletonPatternDemo
 2 {
 3     public class Singleton
 4     {
 5         /// <summary>
 6         /// 利用一个静态变量来记录Singleton类的唯一实例
 7         /// </summary>
 8         private static Singleton _uniqueInstance;
 9 
10         private static readonly object Locker = new object();
11 
12         //这里是其它变量...
13 
14         /// <summary>
15         /// 构造器私有化:只能在类内部才能调用构造器
16         /// </summary>
17         private Singleton() { }
18 
19         /// <summary>
20         /// 只能通过该方法获取到对象实例
21         /// </summary>
22         /// <returns></returns>
23         public static Singleton GetInstance()
24         {
25             //lock:迫使每个线程在进入该方法之前,需要等候别的线程离开该方法,
26             //  也就是说,不会有两个线程可以同时进入该方法
27             lock (Locker)
28             {
29                 if (_uniqueInstance == null)
30                 {
31                     //【注意】如果我们不需要该实例,它就永远不会产生。这就是“延迟实例化”(lazy instantiaze)
32                     _uniqueInstance = new Singleton();
33                 }
34             }
35             
36 
37             return _uniqueInstance;
38 
39         }
40 
41         //这里是其它方法...
42     }
43 }

  但是,现在又出现了性能的问题!...

  方案一:使用“急切”创建实例,而不用延迟实例化的做法

 1 namespace SingletonPatternDemo
 2 {
 3     public class Singleton
 4     {        
 5         //如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,可以选择这种方法
 6 
 7         //在静态初始化器中创建单件,这段代码保证了线程安全
 8         private static readonly Singleton UniqueInstance = new Singleton();
 9 
10         private Singleton() { }
11 
12         public static Singleton GetInstance()
13         {
14             return UniqueInstance;
15         }        
16     }
17 }

  方案二:用“双重检查加锁”

 1 namespace SingletonPatternDemo
 2 {
 3     public class Singleton
 4     {
 5         private static Singleton _uniqueInstance;
 6         private static readonly object Locker = new object();
 7 
 8         private Singleton() { }
 9 
10         public static Singleton GetInstance()
11         {
12             //检查实例,如果不存在,就进入同步区块
13             if (_uniqueInstance == null)
14             {
15                 lock (Locker)
16                 {
17                     if (_uniqueInstance == null)
18                     {
19                         //只有第一次才彻底执行这里的代码
20                         _uniqueInstance = new Singleton();
21                     }
22                 }
23             }                     
24 
25             return _uniqueInstance;
26         }
27 
28     }
29 }

  完毕... ...

posted @ 2015-07-12 22:49  反骨仔  阅读(1132)  评论(2编辑  收藏