• 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 }

      完毕... ...

  • 相关阅读:
    防止头文件的重复包含问题
    git常用命令
    redis
    linux常用操作
    数据库安装
    mysql修改表结构
    mysql 忘记root密码及授权访问
    mysql连表查询
    mysql 存取ip方法
    php批量修改表结构
  • 原文地址:https://www.cnblogs.com/liqingwen/p/4641291.html
Copyright © 2020-2023  润新知