C#设计模式学习笔记:(1)单例模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8250985.html,记录一下学习过程以备后续查用。
一、引言
设计模式的分类:
1)依目的:
创建型(Creational)模式:负责对象创建
结构型(Structural)模式:处理类与对象间的组合
行为型(Behavioral)模式:类与对象交互中的职责分配
2)依范围:
类模式:处理类与子类的静态关系
对象模式:处理对象间的动态关系
注:本系列文章依目的分类来进行。
二、单例模式的介绍
单例模式:英文名称--Singleton Pattern;分类--创建型;定义--一个类仅有一个实例。
2.1、动机(Motivate)
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?这应该是类设计者的责任,而不是使用者的责任。
2.2、意图(Intent)
保证一个类仅有一个实例,并提供一个该实例的全局访问点。--《设计模式GoF》
2.3、结构图(Structure)
2.4、模式组成
这个模式里面只有一个类型,就是Singleton类型,并且这个类只有一个实例,可以通过Instance()方法获取该类型的实例。
2.5、代码实现
既然是单实例,肯定会涉及到多线程的问题。
2.5.1单线程Singleton模式的实现
class Program
{
/// <summary>
/// 单例模式的实现
/// </summary>
public sealed class Singleton
{
//定义一个静态变量来保存类的实例
private static Singleton uniqueInstance; //定义私有构造函数,使外界不能创建该类实例。
private Singleton()
{
Console.WriteLine("Singleton对象已被创建。");
} /// <summary>
/// 定义公有方法提供一个全局访问点,也可以定义公有属性来提供全局访问点。
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//如果类的实例不存在则创建,否则直接返回。
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
} static void Main(string[] args)
{
#region 单例模式
var singleton = Singleton.GetInstance();
Console.Read();
#endregion
}
}
运行结果如下:
私有的实例构造器是屏蔽外界的调用,上面的单例模式的实现在单线程下确实是完美的,也很好的满足了我们单线程环境的需求。
在多线程环境下,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。因为在两个线程同时运行GetInstance方法时,
此时两个线程判断(uniqueInstance==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例。
2.5.2多线程Singleton模式的实现
class Program
{
/// <summary>
/// 单例模式的实现
/// </summary>
public sealed class Singleton
{
//定义一个静态变量来保存类的实例
private static volatile Singleton uniqueInstance; //定义一个标识确保线程同步
private static readonly object locker = new object(); //定义私有构造函数,使外界不能创建该类实例。
private Singleton()
{
Console.WriteLine("Singleton对象已被创建。");
} /// <summary>
/// 定义公有方法提供一个全局访问点,也可以定义公有属性来提供全局访问点。
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//当第一个线程运行到这里时,此时会对locker对象"加锁"。
//当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁。
//lock语句运行完之后(即线程运行完之后)会对该对象"解锁"。
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
} static void Main(string[] args)
{
#region 单例模式
for (int i = ; i <= ; i++)
{
Thread thread = new Thread(new ParameterizedThreadStart(Worker));
thread.Start(i);
}
Console.Read();
#endregion
} private static void Worker(object parameter)
{
Console.WriteLine($"Thread {parameter} is running.");
Singleton.GetInstance();
}
}
运行结果如下:
上面的解决方案确实可以解决多线程的问题,但是上面代码每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,这个是完全没有必要的。
因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假即可,从而减少额外的开销以提高性能。
为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(uniqueInstance==null)的判断,这种双层if加lock的实现方式,我们称它为
“双重锁定(Double Check)”。
class Program
{
/// <summary>
/// 单例模式的实现
/// </summary>
public sealed class Singleton
{
//定义一个静态变量来保存类的实例
private static volatile Singleton uniqueInstance; //定义一个标识确保线程同步
private static readonly object locker = new object(); //定义私有构造函数,使外界不能创建该类实例。
private Singleton()
{
Console.WriteLine("Singleton对象已被创建。");
} /// <summary>
/// 定义公有方法提供一个全局访问点,也可以定义公有属性来提供全局访问点。
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//当第一个线程运行到这里时,此时会对locker对象"加锁"。
//当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁。
//lock语句运行完之后(即线程运行完之后)会对该对象"解锁"。
//双重锁定只需要一句判断就可以了
if (uniqueInstance == null)
{
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
} static void Main(string[] args)
{
#region 单例模式
for (int i = ; i <= ; i++)
{
Thread thread = new Thread(new ParameterizedThreadStart(Worker));
thread.Start(i);
}
Console.Read();
#endregion
} private static void Worker(object parameter)
{
Console.WriteLine($"Thread {parameter} is running.");
Singleton.GetInstance();
}
}
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() { }
}
内联初始化其实是把静态的字段放到静态构造器去初始化。只要想访问静态字段,必定已经在使用之前先执行静态构造器,这样能够精确地保证使用
的时候一定能拿到实例,如果不使用也不会实例化对象,这也就是延时加载的功能。它同样能够支持多线程环境,因为只可能有一个线程执行静态构造
器,不存在多个线程去执行静态构造器(感觉就是程序已经自动为我们加锁了)。
它的一点弊端就是:静态构造器只能声明为一个私有的、无参数的构造器,因而不支持参数化的实例化方法。
需要说明的是: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只考虑对象创建的管理,没有考虑销毁的管理。为什么这样做呢?因为Net平台是支持垃圾回收的,所以我们一般没有必要对其进行销毁
处理。
6)理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。
7)可以很简单的修改一个Singleton,使它有少数几个实例,这样做是允许的而且是有意义的。
5.1、单例模式的优点
1)实例控制:Singleton会阻止其他对象实例化其自己的Singleton对象的副本,从而确保所有对象都访问唯一实例。
2)灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程。
5.2、单例模式的缺点
1)开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销,可以通过使用静态初始化解决此问题。
2)可能的开发混淆:使用Singleton对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无
法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
3)对象的生存期:Singleton不能解决删除单个对象的问题。因为它包含对该静态的私有字段的引用,静态字段是不能被CLR回收内存的,该实
例会和应用程序生命周期一样长,一直存在。
5.3、单例模式的使用场合
1)当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
2)当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
C#设计模式学习笔记:(1)单例模式的更多相关文章
- Java设计模式学习笔记(五) 单例模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 使用单例模式的原因 以Windows任务管理器为例,在Windows系统中,任务管理器是唯 ...
- Java设计模式学习笔记(单例模式)
最近一直在看<Head First设计模式>,这本书写的确实是很不错的,专注于怎么用最简单的方式最通俗的语言让人了解设计模式.据说GoF的设计模式那本书写的很好,是一本经典,但是就是难懂, ...
- C#设计模式学习笔记-单例模式随笔
最近学习 设计模式,从单例模式入手 啥是单例模式: 要实现一个单例类的话,首先,肯定是不能让用户自行生产的,那就是说明不能让用户new,所以,就必须把构造函数设置成为私有的 因为静态变量的生命周期跟整 ...
- C#设计模式学习笔记-单例模式(转)
C#设计模式学习笔记-单例模式 http://www.cnblogs.com/xun126/archive/2011/03/09/1970807.html 最近在学设计模式,学到创建型模式的时候,碰到 ...
- 7 种 Javascript 常用设计模式学习笔记
7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...
- 设计模式学习笔记--备忘录(Mamento)模式
写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...
- Java设计模式学习笔记(二) 简单工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...
- Java设计模式学习笔记(三) 工厂方法模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...
- Java设计模式学习笔记(四) 抽象工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...
- C#设计模式学习笔记:(3)抽象工厂模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...
随机推荐
- [travis-ci]自动集成测试
自动运行测试的平台https://travis-ci.org/ 可以自动导入测试github上的项目 因为yml文件格式错误会导致找不到配置文件, 这里要注意啦, 掉坑里了.... https://d ...
- Android客户端OkHttp的使用以及tomcat服务器的解析客户端发过来的数据
2020-02-15 21:25:42 ### android客户端客户向服务器发送json字符串或者以参数请求的方式发送数据 其中又分为post请求和get请求 1.activity.xml < ...
- 【5min+】 巨大的争议?C# 8 中的接口
系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...
- num06---代理模式
代理模式,比较好理解,关键点就是,被代理类 和 代理类 实现同一个接口,接口中定义着想要实现的被代理的方法,在代理类中引入 被代理类 对象, 最后直接调用代理类的方法即可实现代理功能.
- 关于JAVA中源码级注解的编写及使用
一.注解简介: 1.1.什么是"注解": 在我们编写代码时,一定看到过这样的代码: class Student { private String name; @Override ...
- this的使用情况
this的几种使用情况 1.在普通函数内部,this指向的是window,在严格模式下,this的值是undefined function fun(){ console.log(this); } fu ...
- pytorch之 sava_reload_model
import torch import matplotlib.pyplot as plt # torch.manual_seed(1) # reproducible # fake data x = t ...
- 保留yum安装的软件包
文件路径 /etc/yum.conf [root@opvnserver ~]# grep "keepcache" /etc/yum.conf keepcache=0 [root@o ...
- [Redis-CentOS7]Python操作Redis(十一)
Python 操作redis #!/usr/bin/env pyhton # coding:utf-8 # @Time : 2020-02-16 21:36 # @Author : LeoShi # ...
- Java集合那点事, 满满干货~
说到Java集合,可以说是初学者必踩的坑了. 那么怎么才能学好Java集合呢?个人认为关键是要理清楚思路,从上而下,建立一套完整的思维体系,这样才能更好的去认识事物的本质. 先确立一下学习Java集合 ...