(复制粘贴是最容易的编程,但也是最没有价值的编程。复制粘贴就会造成重复,当需求变化或有 Bug 时就需要改多个地方)

    把声明的工作放到类的全局变量中完成(private FormToolbox ftb;),这样就可以在方法中判断这个变量是否实例化过了。

21.3 是否实例化是自己的责任

    单例类是否实例化过,应该由单例类自己判断。(而不是将判断的代码放到调用类当中)

    (如果已经实例化过,则不需要再实例化)实例化其实就是 new 的过程,而如果不对构造方法做改动的话,是不可能阻止他人不去用 new 的,所以我们完全可以直接把这个类的构造方法改成私有(private)。

  于是,只要将“工具箱”类的构造方法写成 private 的,那么外部程序就不能用 new 来实例化它了。

    对于外部代码,不能用 new 来实例化它,但是我们完全可以再写一个 public 方法,叫做 GetInstance(),这个方法的目的就是返回一个类实例,而此方法中,去做是否有实例化的判断。

  如果没有实例化过,由调用 private 的构造方法 new 出这个实例,之所以它可以调用是因为它们在同一个类中,private 方法可以被调用。

  参考代码:

      public class FormToolbox : Form{private static FormToolbox ftb = null;      //声明一个静态的类变量(全局可用)private FormToolbox()                       //构造方法私有,外部代码不能直接用new来实例化它
          {//InitializeComponent();    //这个方法也许只有窗体才有,其他的类只要空着就行
          }public static FormToolbox GetInstance()     //得到类实例的方法,返回值就是本类对象,注意也是静态的
          {if(ftb == null || ftb.IsDisposed){ftb = new FormToolbox();            //当内部的ftb是null或者被Disposed过,则new它。此时将实例化的对象存在静态的变量ftb中,以后就可以不用实例化而得到它//可以在这里继续设置ftb的其他属性
              }return ftb;}}

    (当窗体关闭后,实例并没有变为 null,而只是 Disposed —— if (ftb == null || ftb.IsDisposed))

    这样一来,客户端不再考虑是否需要去实例化的问题,而把责任都给了应该负责的类去处理。这就是单例模式。

21.4 单例模式

    单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。(因为是全局访问点,所以该方法必须是静态的)

    通常,我们可以使用一个全局变量(static)使得一个对象被访问,但它不能防止你实例化多个对象(对象的属性也可能会发生改变)。一个最好的办法就是,让类自身负责保存它的唯一实例。

  这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法(即返回该实例的静态方法)。

    单例模式的好处:

      因为单例模式封装了它的唯一实例,所以它可以严格地控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。

    单例与实用类(如Math类)的静态方法类似,实用类通常也会采用私有化的构造方法来避免其有实例。

    但它们也有很多的不同,比如:

      实用类不保存状态,仅提供一些静态方法或静态属性让你使用,而单例类是有状态的。

      实用类不能用于继承多态,而单例虽然实例唯一,却是可以有子类来继承。

      实用类只不过是一些方法、属性的集合,而单例却是有着唯一对象的实例。

★21.5 多线程时的单例

    另外,还需要注意一些细节,比如说,多线程的程序中,当多个线程同时(注意是同时)访问单例类,调用GetInstance()方法,是会有可能造成创建多个实例的。

    解决的方法是给进程加锁来处理:

      lock 语句的含义—— lock 可以确保当一个线程位于代码的临界区时,另一个线程不进入临界区。(如果其他线程视图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放)

    (当写在了 .NET 网站程序的启动部分时应该就不用考虑这个多线程的问题了,因为它的实例化并不是由线程发起的)

      public class Singleton{private static Singleton instance;private static readonly object syncRoot = new object();     //程序运行时创建一个静态只读的进程辅助对象private Singleton(){}public static Singleton GetInstance(){lock(syncRoot)                          //在同一个时刻,加了锁的那部分程序只有一个线程可以进入
              {if(instance==null){instance = new Singleton();}}return instance;}}

    由于有了 lock,就保证了多线程环境下的同时访问也不会造成多个实例的生成。(我们并不知道 instance 实例有没有被创建过,所以无法对它加锁,于是创建了一个 syncRoot 并选择对其加锁)

    但是,当前这种写法,每次调用 GetInstance 方法时都需要加锁,这种做法是会影响性能的,所以可以对这个类做如下改良:

21.6 双重锁定

    public class Singleton{private static Singleton instance;private static readonly object syncRoot = new object();     //程序运行时创建一个静态只读的进程辅助对象private Singleton(){}public static Singleton GetInstance(){if(instance==null)              //先判断实例是否存在,不存在时再加锁处理
            {lock (syncRoot)                          //在同一个时刻,加了锁的那部分程序只有一个线程可以进入
                {if (instance == null){instance = new Singleton();}}}return instance;}}

    现在这样就不用每次都让线程加锁了,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。这种做法被称为 Double-Check Locking(双重锁定)

    这里为什么要两次判断 instance 是否为空呢?

      当 instance 为 null 并且同时有两个线程调用 GetInstance()方法时,它们将都可以通过第一重 instance == null 的判断。然后由于 lock 机制,这两个线程只有一个进入,另一个在外排队等候,必须要其中的一个进入并出来后,另一个才能进入。

    而此时如果没有了第二重的 instance 是否为 null 的判断,则第一个线程创建了实例,而第二个线程还是可以继续再创建新的实例,这就没有达到单例的目的。

21.7 静态初始化

    静态初始化的实现方式更为简单,同时也有着与单例模式类似的特性,但两者仍有一些不同,比如静态初始化只能在静态初始化期间或在类构造函数中分配变量(instance 变量标记为 readonly)

附加:单例模式的继承

  父类:

public class Singleton<T> where T : class, new()
{private static T _instance;  //使用了泛型T,而子类中又用自身进行泛型,所以这里可以直接将 _instance 理解为子类实例(也是子类唯一实例)private static readonly object syslock = new object();public static T getInstance(){   //线程安全锁if (_instance == null){lock (syslock){if (_instance == null){_instance = new T();}}}return _instance;}
}

  子类需要泛型一下:

public class Two : Singleton<Two>
{public void Show(){Console.WriteLine("Two Sub class.......");}
}

  调用:

    Two.getInstance().Show();

转载于:https://www.cnblogs.com/zhangchaoran/p/8465491.html

第 21 章 —— 单例模式相关推荐

  1. 复现经典:《统计学习方法》第21章 PageRank算法

    第21章 PageRank算法 本文是李航老师的<统计学习方法>一书的代码复现.作者:黄海广 备注:代码都可以在github中下载.我将陆续将代码发布在公众号"机器学习初学者&q ...

  2. 软件开发计划_敏捷软件开发实践:估算与计划读书笔记123第21章 关于计划的沟通...

    <敏捷软件开发实践:估算与计划>第21章 关于计划的沟通,重点和要点的思维导图及文字内容. 第21章 关于计划的沟通 The more elaborate our means of com ...

  3. 信安教程第二版-第21章网络设备安全

    第21章 网络设备安全 21.1 网络设备安全概况 447 21.1.1 交换机安全威胁 447 21.1.2 路由器安全威胁 448 21.2 网络设备安全机制与实现技术 449 21.2.1 认证 ...

  4. 【设计模式】第三章 单例模式

    第三章 单例模式 文章目录 第三章 单例模式 一.介绍 二.实现 1.饿汉式 2.双检锁 3.静态内部类 4.枚举 三.改进 1.防止反射.克隆.序列化对单例模式的破坏 一.介绍 单例模式(Singl ...

  5. 第 21 章 中介者模式

    第 21 章 中介者模式 1.智能家庭项目 智能家庭项目: 智能家庭包括各种设备,闹钟.咖啡机.电视机.窗帘 等 主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为: 闹铃响起 ...

  6. k - 老鼠走迷宫_《另一个伊甸:超越时空的猫》第21章地图星之塔完整迷宫攻略...

    另一个伊甸:超越时空的猫这款手游在上线国服之后受到了很多玩家的关注,今天就为大家介绍一下第21章地图星之塔完整迷宫攻略,希望能够给大家带来帮助. 第21章地图星之塔完整迷宫攻略 「星之塔」相较于「时之 ...

  7. 第21章,DNS服务

    更多内容请点击: Linux学习从入门到打死也不放弃,完全笔记整理(持续更新,求收藏,求点赞~~~~) https://blog.51cto.com/13683480/2095439 第21章,DNS ...

  8. 读书笔记:《流畅的Python》第21章 类元编程

    # 第21章 类元编程""" 类元编程指的是运行时创建或定制类的技艺1.类是一等对象,任何时候都可以使用函数新建类,而无需使用class关键字2.类装饰器也是函数,不过能 ...

  9. xsemaphoretake返回_【FreeRTOS操作系统教程】第21章 FreeRTOS计数信号量

    第21章 FreeRTOS计数信号量 本章节开始讲解FreeRTOS任务间的同步和资源共享机制,计数信号量.FreeRTOS中计数信号量的源码实现是基于消息队列实现的. 本章教程配套的例子含Corte ...

最新文章

  1. 号外:Mapinfo被Pitney Bowes公司收购
  2. Windows Phone 7 开发 31 日谈——第6日:工具栏
  3. jvm性能调优 - 14JVM的老年代垃圾回收器CMS原理
  4. 剑桥大学国际学生事务部网站
  5. OpenCASCADE绘制测试线束:几何命令之转换
  6. gdb调试器命令(zz)
  7. Leetcode--209. 长度最小的子数组
  8. php静态资源服务器,Node实现静态资源服务器
  9. linux 系统错误表 和对应的数值
  10. Jenkins持续集成之小试牛刀
  11. java性能测试jmh
  12. 学习《让UpdatePanel支持文件上传》系列文章的相关链接
  13. BigDecimal浮点精度加减乘除运算
  14. STM32串口通信实例
  15. 非极大值抑制(PyTorch-YOLOv3代码解析一)
  16. unicode 表情对照表
  17. CS5218: DP转HDMI 4K30HZ转换方案
  18. 美国出台商用无人机新规,宝宝表示我也要去考无人机飞行员驾照
  19. 鸿蒙os能支持c语言吗,再次确认华为鸿蒙OS系统采用C预言开发,流畅度相当ios
  20. 微信小程序请求及封装请求方式

热门文章

  1. CSS基础——position位置属性
  2. java 中文乱码问题,请注意response.getWriter的顺序
  3. Sqlserver循环嵌套
  4. PowerDesigner生成数据库建表sql脚本
  5. 语法:MySQL中INSERT IGNORE INTO和REPLACE INTO的使用
  6. 如何在国内跑Kubernetes的minikube
  7. spring 链接池
  8. 一个简单视频网站开发小记
  9. spring和mybatis整合进行事务管理
  10. g_thread_init