第 21 章 —— 单例模式
(复制粘贴是最容易的编程,但也是最没有价值的编程。复制粘贴就会造成重复,当需求变化或有 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 章 —— 单例模式相关推荐
- 复现经典:《统计学习方法》第21章 PageRank算法
第21章 PageRank算法 本文是李航老师的<统计学习方法>一书的代码复现.作者:黄海广 备注:代码都可以在github中下载.我将陆续将代码发布在公众号"机器学习初学者&q ...
- 软件开发计划_敏捷软件开发实践:估算与计划读书笔记123第21章 关于计划的沟通...
<敏捷软件开发实践:估算与计划>第21章 关于计划的沟通,重点和要点的思维导图及文字内容. 第21章 关于计划的沟通 The more elaborate our means of com ...
- 信安教程第二版-第21章网络设备安全
第21章 网络设备安全 21.1 网络设备安全概况 447 21.1.1 交换机安全威胁 447 21.1.2 路由器安全威胁 448 21.2 网络设备安全机制与实现技术 449 21.2.1 认证 ...
- 【设计模式】第三章 单例模式
第三章 单例模式 文章目录 第三章 单例模式 一.介绍 二.实现 1.饿汉式 2.双检锁 3.静态内部类 4.枚举 三.改进 1.防止反射.克隆.序列化对单例模式的破坏 一.介绍 单例模式(Singl ...
- 第 21 章 中介者模式
第 21 章 中介者模式 1.智能家庭项目 智能家庭项目: 智能家庭包括各种设备,闹钟.咖啡机.电视机.窗帘 等 主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为: 闹铃响起 ...
- k - 老鼠走迷宫_《另一个伊甸:超越时空的猫》第21章地图星之塔完整迷宫攻略...
另一个伊甸:超越时空的猫这款手游在上线国服之后受到了很多玩家的关注,今天就为大家介绍一下第21章地图星之塔完整迷宫攻略,希望能够给大家带来帮助. 第21章地图星之塔完整迷宫攻略 「星之塔」相较于「时之 ...
- 第21章,DNS服务
更多内容请点击: Linux学习从入门到打死也不放弃,完全笔记整理(持续更新,求收藏,求点赞~~~~) https://blog.51cto.com/13683480/2095439 第21章,DNS ...
- 读书笔记:《流畅的Python》第21章 类元编程
# 第21章 类元编程""" 类元编程指的是运行时创建或定制类的技艺1.类是一等对象,任何时候都可以使用函数新建类,而无需使用class关键字2.类装饰器也是函数,不过能 ...
- xsemaphoretake返回_【FreeRTOS操作系统教程】第21章 FreeRTOS计数信号量
第21章 FreeRTOS计数信号量 本章节开始讲解FreeRTOS任务间的同步和资源共享机制,计数信号量.FreeRTOS中计数信号量的源码实现是基于消息队列实现的. 本章教程配套的例子含Corte ...
最新文章
- 号外:Mapinfo被Pitney Bowes公司收购
- Windows Phone 7 开发 31 日谈——第6日:工具栏
- jvm性能调优 - 14JVM的老年代垃圾回收器CMS原理
- 剑桥大学国际学生事务部网站
- OpenCASCADE绘制测试线束:几何命令之转换
- gdb调试器命令(zz)
- Leetcode--209. 长度最小的子数组
- php静态资源服务器,Node实现静态资源服务器
- linux 系统错误表 和对应的数值
- Jenkins持续集成之小试牛刀
- java性能测试jmh
- 学习《让UpdatePanel支持文件上传》系列文章的相关链接
- BigDecimal浮点精度加减乘除运算
- STM32串口通信实例
- 非极大值抑制(PyTorch-YOLOv3代码解析一)
- unicode 表情对照表
- CS5218: DP转HDMI 4K30HZ转换方案
- 美国出台商用无人机新规,宝宝表示我也要去考无人机飞行员驾照
- 鸿蒙os能支持c语言吗,再次确认华为鸿蒙OS系统采用C预言开发,流畅度相当ios
- 微信小程序请求及封装请求方式