C#设计模式之一单例模式(Singleton Pattern)【创建型】
一、引言
看了李建忠老师的讲的设计模式已经有一段时间了(这段时间大概有一年多了),自己还没有写过自己的、有关设计模式的文章。这次想写一些关于设计模式的文章,用自己的理解和代码来写,算是复习一遍。写作的过程中也会多看看其他大牛的文章,争取让自己的理解正确,否则把大家带跑偏了,就是我的过错了。今天就开始我们第一个设计模式,该模式是:【单例模式】,英文名称:Singleton
Pattern,这个模式很简单,一个类型只需要一个实例,他是创建型的设计模式。为什么叫“创建型”设计模式呢,因为他们有分类。当然了分类的方式不一样,分类的结果也就不一样。
从目的来看:
-创建型(Creational)模式:负责对象创建
-结构型(Structural)模式:处理类与对象间的组合
-行为型(Behavioral)模式:类与对象交互中的职责分配
从范围来看:
-类模式处理类与子类的静态关系
-对象模式处理对象间的动态关系
以上就是分类的方式,我们按大多数的分类,采用“从目的来看”的分类来对设计模式进行分类,我们就开始今天的学习吧。
二、单例模式的介绍
2.1、动机(Motivate)
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?
这应该是类设计者的责任,而不是使用者的责任
2.2、意图(Intent)
保证一个类仅有一个实例,并提供一个该实例的全局访问点。 --《设计模式GoF》
2.3、结构图(Structure)
2.4、模式的组成
(1)、单件实例(Singleton):这个模式里面只有一个类型,就是Singleton类型,并且这个类只有一个实例,可以通过Instance()方法获取该类型的实例。
2.5、单件模式的代码实现
既然是单实例,肯定会涉及到多线程的问题,我们就一步一步的来写代码,我们先看看单线程Singleton模式的实现,代码如下:
1 /// <summary>
2 /// 单例模式的实现
3 /// </summary>
4 public sealed class Singleton
5 {
6 // 定义一个静态变量来保存类的实例
7 private static Singleton uniqueInstance;
8
9 // 定义私有构造函数,使外界不能创建该类实例
10 private Singleton()
11 {
12 }
13
14 /// <summary>
15 /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
16 /// </summary>
17 /// <returns></returns>
18 public static Singleton GetInstance()
19 {
20 // 如果类的实例不存在则创建,否则直接返回
21 if (uniqueInstance == null)
22 {
23 uniqueInstance = new Singleton();
24 }
25 return uniqueInstance;
26 }
27 }
私有的实例构造器是屏蔽外界的调用,上面的单例模式的实现在单线程下确实是完美的,也很好的满足了我们单线程环境的需求。
单线程单例模式的几个要点:
(1)、Singleton模式中的实例构造器可以设置为protected以允许子类派生。
(2)、Singleton模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。
(3)、Singleton模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷违背。
(4)、Singletom模式只考虑到了对象创建的工作,没有考虑对象销毁的工作。为什么这样做呢,因为Net平台是支持垃圾回收的,所以我们一般没有必要对其进行销毁处理。
(5)、不能应对多线程环境:在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象
如果放在多线程环境下,问题就出来了。因为在两个线程同时运行GetInstance方法时,此时两个线程判断(uniqueInstance
==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了。要想解决这个问题,只要让GetInstance方法在同一时间只运行一个线程运行就好了,让我们看看多线程Singleton模式的实现,代码如下:
1 /// <summary>
2 /// 单例模式的实现
3 /// </summary>
4 public sealed class Singleton
5 {
6 // 定义一个静态变量来保存类的实例
7 private static volatile Singleton uniqueInstance;
8
9 // 定义一个标识确保线程同步
10 private static readonly object locker = new object();
11
12 // 定义私有构造函数,使外界不能创建该类实例
13 private Singleton()
14 {
15 }
16
17 /// <summary>
18 /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
19 /// </summary>
20 /// <returns></returns>
21 public static Singleton GetInstance()
22 {
23 // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
24 // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
25 // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
26 lock (locker)
27 {
28 // 如果类的实例不存在则创建,否则直接返回
29 if (uniqueInstance == null)
30 {
31 uniqueInstance = new Singleton();
32 }
33 }
34
35 return uniqueInstance;
36 }
37 }
上面这种解决方案确实可以解决多线程的问题,但是上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(uniqueInstance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定(Double Check)”,下面具体看看实现代码的:
1 /// <summary>
2 /// 单例模式的实现
3 /// </summary>
4 public sealed class Singleton
5 {
6 // 定义一个静态变量来保存类的实例
7 private static volatile Singleton uniqueInstance;
8
9 // 定义一个标识确保线程同步
10 private static readonly object locker = new object();
11
12 // 定义私有构造函数,使外界不能创建该类实例
13 private Singleton()
14 {
15 }
16
17 /// <summary>
18 /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
19 /// </summary>
20 /// <returns></returns>
21 public static Singleton GetInstance()
22 {
23 // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
24 // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
25 // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
26 // 双重锁定只需要一句判断就可以了
27 if (uniqueInstance == null)
28 {
29 lock (locker)
30 {
31 // 如果类的实例不存在则创建,否则直接返回
32 if (uniqueInstance == null)
33 {
34 uniqueInstance = new Singleton();
35 }
36 }
37 }
38 return uniqueInstance;
39 }
40 }
volatile修饰:编译器在编译代码的时候会对代码的顺序进行微调,用volatile修饰保证了严格意义的顺序。一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
三、C#中实现了单例模式的类
现在我们看看,如何使用C#语言的特性来实现单例的Singleton模式。
//Singleton模式的实现
public sealed class Singleton
{
public static readonly Singleton instance=new Singleton(); private Singleton(){}
} 以上是内联初始化(生成的同时进行初始化)的单例模式,它等同于: public sealed class Singleton
{
public static readonly Singleton instance; //静态构造函数,CLR只执行一次
static Singleton()
{
instance=new Singleton();
} //私有构造函数,防止外界调用
private Singleton(){}
}
内联初始化其实是把静态的字段放到静态构造器去初始化。只要想访问静态字段,必定已经在之前执行了静态构造器。这样也能够精确地保证使用的时候一定能拿到实例,如果不使用也不会实例化对象,也就是延时加载的功能。他同样能够支持多线程环境,因为只可能有一个线程执行静态构造器,不可能有多个线程去执行静态构造器,感觉就是程序已经自动为我们加锁了。
它的一点弊端就是它不支持参数化的实例化方法。在.NET里静态构造器只能声明一个,而且必须是无参数的,私有的。因此这种方式只适用于无参数的构造器。
需要说明的是:HttpContext.Current就是一个单例,他们是通过Singleton的扩展方式实现的,他们的单例也并不是覆盖所有领域,只是针对某些局部领域中,是单例的,不同的领域中还是会有不同的实例。
四、Singleton模式的扩展
(1)、将一个实例扩展到n个实例,例如对象池的实现。(n不是指无限个实例,而是固定的某个数)
(2)、将new构造器的调用转移到其他类中,例如多个类协同工作环境中,某个局部环境只需要拥有某个类的一个实例。
(3)、理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。
五、单例模式的实现要点
1、Singleton模式是限制而不是改进类的创建。
2、Singleton类中的实例构造器可以设置为Protected以允许子类派生。
3、Singleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背。
4、Singleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Singleton模式的初衷违背。
5、Singleton只考虑了对象创建的管理,没有考虑到销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。
6、理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。
7、可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。
1】、单例模式的优点:
(1)、实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例
(2)、灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
2】、单例模式的缺点:
(1)、开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
(2)、可能的开发混淆:使用 singleton 对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
(3)、对象的生存期:Singleton 不能解决删除单个对象的问题。因为它包含对该静态的私有字段的引用,静态字段是不能被CLR回收内存的,该实例会和应用程序生命周期一样长,一直存在。
3】、单例模式的使用场合:
(1)、当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
(2)、当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
六、总结
到这里,单例模式就介绍完了,这个模式很简单,理解起来也不是很难,只要把握住代码的实现技巧,一般问题都不大,但是要找好使用的时机,如果使用错误,一些逻辑错误比较难排查。
C#设计模式之一单例模式(Singleton Pattern)【创建型】的更多相关文章
- 乐在其中设计模式(C#) - 单例模式(Singleton Pattern)
原文:乐在其中设计模式(C#) - 单例模式(Singleton Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 单例模式(Singleton Pattern) 作者:weba ...
- 【设计模式】单例模式 Singleton Pattern
通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance) 的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...
- C#面向对象设计模式纵横谈——2.Singleton 单件(创建型模式)
一:模式分类 从目的来看: 创建型(Creational)模式:负责对象创建. 结构型(Structural)模式:处理类与对象间的组合. 行为型(Behavioral)模式:类与对象交互中的职责分配 ...
- 设计模式之单例模式(Singleton Pattern)
单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...
- 二十四种设计模式:单例模式(Singleton Pattern)
单例模式(Singleton Pattern) 介绍保证一个类仅有一个实例,并提供一个访问它的全局访问点. 示例保证一个类仅有一个实例. Singleton using System; using S ...
- 设计模式--单例模式Singleton(创建型)
单例模式很显然是定义一个类,这个类在程序中只有唯一的实例对象.一般单例类的构造函数是私有的,只能通过调用静态函数GetInstance来获取实例. 一.单例模式有三种:懒汉式单例.饿汉式单例.登记式单 ...
- 设计模式(二)单例模式Singleton(创建型)
几乎所有面向对象的程序中,总有一些类的对象需要是唯一的,例如,通过数据库句柄到数据库的连接是独占的.您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销.再如大家最经常用的IM, ...
- 设计模式-单例模式(Singleton) (创建型模式)
//以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Singleton.h #pragma once #include<iostream> class Sin ...
- Java 设计模式(三)-单例模式(Singleton Pattern)
1 概念定义 1.1 定义 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 1.2 类型 创建类模式 1.3 难点 1)多个虚拟机 当系统中的单例类被拷贝运行在多 ...
- 单例模式/singleton模式/创建型模式
Java实现要点: 私有构造方法 线程安全(并发的考虑) 延迟加载(效率的考虑,对于较大的类在使用时在加载) 公有方法访问单一实例 常见单例模式代码及问题 //无延迟加载,常驻内存(即使不使用) cl ...
随机推荐
- Selenium中如何使用xpath更快定位
在学习Selenium路上,踩了也不少坑,这是我最近才发现的一个新写法,好吧,"才发现"又说明我做其他事了.对的,我现在还在加班! 开车~~~ 例子:知乎网 标签:Python3. ...
- QQ互联申请及配置
今天要说的只是针对QQ互联的操作,其他的互联请参考相关网站. 第一步:需要申请API接口的两码 自行登录QQ互联https://connect.qq.com/index.html,然后按照要求申请就O ...
- java线程池的创建使用
利用java的多线程编程可以大大的提高系统的并发运行效率,线程越多并发执行的任务就越多,但是并不意味着效率会一直提高,相反会得到适得其反的效果. java中的多线程编程一共有三种方法: 继承Threa ...
- KMP算法具体解释
这几天学习kmp算法,解决字符串的匹配问题.開始的时候都是用到BF算法,(BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配 ...
- zookeeper web ui-->node-zk-browser安装
眼下公司正在使用zookeeper做配置管理和其它工作,在网上找几个zookeeper管理工具,都不尽人意,要么功能不够强大,要么不能友好的浏览zk树形结构.我的想法是zk管理工具,应该有一个树形结构 ...
- 观未见,行不止 —— Power BI 两周年技术和方案交流圆桌会议纪实
作者:陈希章 发表于 2017年8月13日 2017年8月11日下午两点,Power BI 两周年技术和方案交流圆桌会议如期举行.线上和线下约有100位朋友参加了由我组织和主持的本次活动,在两个小时的 ...
- [UWP]了解模板化控件(6):使用附加属性
1. 基本需求 之前的ContentView2添加了PointerOver等效果,和TextBox等本来就有Header的控件放在一起反而变得鹤立鸡群. 为了解决这个问题,这次把ContentView ...
- Material使用07 MatGridListModule的使用
1 MatGridListModule简介 对相似数据的展现,尤其是像是图片的展示 使用起来很像表格 官方文档:点击前往 2 MatGridListModule提供的指令 2.1 mat-grid-l ...
- docker commit使用
我们运行的容器可能在镜像的基础上做了一些修改,有时候我们希望保存起来,封装成一个更新的镜像 docker自己提供的有commit功能 我们以centos为例,现在我们要在一个裸的centos上面安装v ...
- rwx读写执行对文件和目录的意义
文件 目录 r 查看 列出目录内容 w 修改 在目录内新建删除文件 x 执行 可以进入目录 对文件的删除权限是对文件所有目录的写权限 对目录-wx的权限,有写和执行权限,既可以在目录内创建删除文件,可 ...