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 }

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

C#设计模式:单件(例)模式 -- 类也玩计划生育
 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 }

  完毕... ...

你可能感兴趣的:(设计模式)