身边的设计模式(一):单例 与 RedisCacheManager

大家好,以后我会用23篇文章,来给大家讲解设计模式,当然如果你看过我的项目,很多设计模式已经很会了,只是没有注意到,我这里会讲解一下,大家就会发现,如果你看懂了我的项目,其实已经至少学会了六种设计模式了。

 一、什么是单例模式

【单例模式】,英文名称:Singleton Pattern,这个模式很简单,一个类型只需要一个实例,他是属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)。

  • 1、单例类只能有一个实例。

  • 2、单例类必须自己创建自己的唯一实例。

  • 3、单例类必须给所有其他对象提供这一实例。

那咱们大概知道了,其实说白了,就是我们整个项目周期内,只会有一个实例,当项目停止的时候,实例销毁,当重新启动的时候,我们的实例又会产品。

上文中说到了一个名词【创建类型】的设计模式,那什么是创建类型的设计模式呢?

创建型(Creational)模式:负责对象创建,我们使用这个模式,就是为了创建我们需要的对象实例的。

那除了创建型还有其他两种类型的模式:

  -结构型(Structural)模式:处理类与对象间的组合

  -行为型(Behavioral)模式:类与对象交互中的职责分

这两种设计模式,以后会慢慢说到,这里先按下不表。

咱们就重点从0开始分析分析如何创建一个单例模式的对象实例。

 二、如何创建单例模式

实现单例模式有很多方法:从“懒汉式”到“饿汉式”,最后“双检锁”模式。

这里咱们就慢慢的,从一步一步的开始讲解如何创建单例,既然要创建单一的实例,那我们首先需要学会如何去创建一个实例,这个很简单,相信每个人都会创建实例,就比如说这样的:

 /// <summary>	/// 定义一个天气类	/// </summary>	public class WeatherForecast	{	public DateTime Date { get; set; } = DateTime.Now;	public int TemperatureC { get; set; }	public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);	public string Summary { get; set; }	}	[HttpGet]	
public WeatherForecast Get()	
{	// 实例化一个对象实例	WeatherForecast weather = new WeatherForecast();	return weather;	
}	

我们每次访问的时候,时间都是会变化,所以我们的实例也是一直在创建,在变化,

640?wx_fmt=gif

相信每个人都能看到这个代码是什么意思,不多说,直接往下走,我们知道,单例模式的核心目的就是:

必须保证这个实例在整个系统的运行周期内是唯一的,这样可以保证中间不会出现问题。

那好,我们改进改进,不是说要唯一一个么,好说!我直接返回不就行了:

 /// <summary>	/// 定义一个天气类	/// </summary>	public class WeatherForecast	{	// 定义一个静态变量来保存类的唯一实例	private static WeatherForecast uniqueInstance;	// 定义私有构造函数,使外界不能创建该类实例	private WeatherForecast()	{	}	/// <summary>	/// 静态方法,来返回唯一实例	/// 如果存在,则返回	/// </summary>	/// <returns></returns>	public static WeatherForecast GetInstance()	{	// 如果类的实例不存在则创建,否则直接返回	// 其实严格意义上来说,这个不属于【单例】	if (uniqueInstance == null)	{	uniqueInstance = new WeatherForecast();	}	return uniqueInstance;	}	public DateTime Date { get; set; } = DateTime.Now;	public int TemperatureC { get; set; }	public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);	public string Summary { get; set; }	}	

然后我们修改一下调用方法,因为我们的默认构造函数已经私有化了,不允许再创建实例了,所以我们直接这么调用:

[HttpGet]	public WeatherForecast Get()	{	// 实例化一个对象实例	WeatherForecast weather = WeatherForecast.GetInstance();	return weather;	}	

最后来看看效果:

640?wx_fmt=gif

这个时候,我们可以看到,时间已经不发生变化了,也就是说我们的实例是唯一的了,大功告成!是不是很开心!

但是,别着急,问题来了,我们目前是单线程的,所以只有一个,那如果多线程呢,如果多个线程同时访问,会不会也会正常呢?

这里我们做一个测试,我们在项目启动的时候,用多线程去调用:

  public WeatherForecast Get()	{	// 实例化一个对象实例	//WeatherForecast weather = WeatherForecast.GetInstance();	// 多线程去调用	for (int i = 0; i < 3; i++)	{	var th = new Thread(	new ParameterizedThreadStart((state) =>	{	WriteWeather();	})	);	th.Start(i);	}	return null;	}

然后我们看看效果是怎样的,按照我们的思路,应该是只会走一遍构造函数,其实不是:

640?wx_fmt=gif

3个线程在第一次访问GetInstance方法时,同时判断(uniqueInstance ==null)这个条件时都返回真,然后都去创建了实例,这个肯定是不对的。那怎么办呢,只要让GetInstance方法只运行一个线程运行就好了,我们可以加一个锁来控制他,代码如下:

public class WeatherForecast	{	// 定义一个静态变量来保存类的唯一实例	private static WeatherForecast uniqueInstance;	// 定义一个锁,防止多线程	private static readonly object locker = new object();	// 定义私有构造函数,使外界不能创建该类实例	private WeatherForecast()	{	}	/// <summary>	/// 静态方法,来返回唯一实例	/// 如果存在,则返回	/// </summary>	/// <returns></returns>	public static WeatherForecast GetInstance()	{	// 当第一个线程执行的时候,会对locker对象 "加锁",	// 当其他线程执行的时候,会等待 locker 执行完解锁	lock (locker)	{	// 如果类的实例不存在则创建,否则直接返回	if (uniqueInstance == null)	{	uniqueInstance = new WeatherForecast();	}	}	return uniqueInstance;	}	public DateTime Date { get; set; } = DateTime.Now;	public int TemperatureC { get; set; }	public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);	public string Summary { get; set; }	}

这个时候,我们再并发测试,发现已经都一样了,这样就达到了我们想要的效果,但是这样真的是最完美的么,其实不是的,因为我们加锁,只是第一次判断是否为空,如果创建好了以后,以后就不用去管这个 lock 锁了,我们只关心的是 uniqueInstance 是否为空,那我们再完善一下:

 /// <summary>	/// 定义一个天气类	/// </summary>	public class WeatherForecast	{	// 定义一个静态变量来保存类的唯一实例	private static WeatherForecast uniqueInstance;	// 定义一个锁,防止多线程	private static readonly object locker = new object();	// 定义私有构造函数,使外界不能创建该类实例	private WeatherForecast()	{	}	/// <summary>	/// 静态方法,来返回唯一实例	/// 如果存在,则返回	/// </summary>	/// <returns></returns>	public static WeatherForecast GetInstance()	{	// 当第一个线程执行的时候,会对locker对象 "加锁",	// 当其他线程执行的时候,会等待 locker 执行完解锁	if (uniqueInstance == null)	{	lock (locker)	{	// 如果类的实例不存在则创建,否则直接返回	if (uniqueInstance == null)	{	uniqueInstance = new WeatherForecast();	}	}	}	return uniqueInstance;	}	public DateTime Date { get; set; } = DateTime.Now;	public int TemperatureC { get; set; }	public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);	public string Summary { get; set; }	}

这样才最终的完美实现我们的单例模式!搞定。

 三、我们在哪里遇到过?

如果你看过我的 Blog.Core 项目的话,肯定看到过 Redis 那部分,我在那里就是封装了一个单例,感兴趣的可以看看:

public class RedisCacheManager : IRedisCacheManager	{	private readonly string redisConnenctionString;	public volatile ConnectionMultiplexer redisConnection;	private readonly object redisConnectionLock = new object();	public RedisCacheManager()	{	string redisConfiguration = Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "ConnectionString" });//获取连接字符串	if (string.IsNullOrWhiteSpace(redisConfiguration))	{	throw new ArgumentException("redis config is empty", nameof(redisConfiguration));	}	this.redisConnenctionString = redisConfiguration;	this.redisConnection = GetRedisConnection();	}	/// <summary>	/// 核心代码,获取连接实例	/// 通过双if 夹lock的方式,实现单例模式	/// </summary>	/// <returns></returns>	private ConnectionMultiplexer GetRedisConnection()	{	//如果已经连接实例,直接返回	if (this.redisConnection != null && this.redisConnection.IsConnected)	{	return this.redisConnection;	}	//加锁,防止异步编程中,出现单例无效的问题	lock (redisConnectionLock)	{	if (this.redisConnection != null)	{	//释放redis连接	this.redisConnection.Dispose();	}	try	{	this.redisConnection = ConnectionMultiplexer.Connect(redisConnenctionString);	}	catch (Exception)	{	//throw new Exception("Redis服务未启用,请开启该服务,并且请注意端口号,本项目使用的的6319,而且我的是没有设置密码。");	}	}	return this.redisConnection;	}	/// <summary>	/// 清除	/// </summary>	public void Clear()	{	foreach (var endPoint in this.GetRedisConnection().GetEndPoints())	{	var server = this.GetRedisConnection().GetServer(endPoint);	foreach (var key in server.Keys())	{	redisConnection.GetDatabase().KeyDelete(key);	}	}	}	/// <summary>	/// 判断是否存在	/// </summary>	/// <param name="key"></param>	/// <returns></returns>	public bool Get(string key)	{	return redisConnection.GetDatabase().KeyExists(key);	}	/// <summary>	/// 查询	/// </summary>	/// <param name="key"></param>	/// <returns></returns>	public string GetValue(string key)	{	return redisConnection.GetDatabase().StringGet(key);	}	/// <summary>	/// 获取	/// </summary>	/// <typeparam name="TEntity"></typeparam>	/// <param name="key"></param>	/// <returns></returns>	public TEntity Get<TEntity>(string key)	{	var value = redisConnection.GetDatabase().StringGet(key);	if (value.HasValue)	{	//需要用的反序列化,将Redis存储的Byte[],进行反序列化	return SerializeHelper.Deserialize<TEntity>(value);	}	else	{	return default(TEntity);	}	}	/// <summary>	/// 移除	/// </summary>	/// <param name="key"></param>	public void Remove(string key)	{	redisConnection.GetDatabase().KeyDelete(key);	}	/// <summary>	/// 设置	/// </summary>	/// <param name="key"></param>	/// <param name="value"></param>	/// <param name="cacheTime"></param>	public void Set(string key, object value, TimeSpan cacheTime)	{	if (value != null)	{	//序列化,将object值生成RedisValue	redisConnection.GetDatabase().StringSet(key, SerializeHelper.Serialize(value), cacheTime);	}	}	/// <summary>	/// 增加/修改	/// </summary>	/// <param name="key"></param>	/// <param name="value"></param>	/// <returns></returns>	public bool SetValue(string key, byte[] value)	{	return redisConnection.GetDatabase().StringSet(key, value, TimeSpan.FromSeconds(120));	}	}

好啦,今天的设计模式你学会了么,在我的项目中,还隐藏了,工厂模式,装饰器模式,中介者模式,观察者模式,享元模式等等等等,下次再见咯。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313011.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes包管理器Helm发布3.0版本

Helm 3.0 已经发布&#xff0c;该版本是 CLI 工具的最新主要版本&#xff0c;主要关注简单性、安全性和可用性&#xff0c;内容如下&#xff1a;新特性移除 Tiller&#xff08;Helm 2 是一种 Client-Server 结构&#xff0c;客户端称为 Helm&#xff0c;服务器称为 Ti…

“兼职”运维的常用命令

自从产品转到了 dotNET Core 之后&#xff0c;更深入的接触 Linux和 Docker &#xff0c;而我每天的工作中&#xff0c;有一部分时间相当于在“兼职”做一些运维的事情。下面是一些在日常中常用的命令&#xff0c;算是个备忘吧。环境操作系统&#xff1a;CentOS7Docker&#xf…

rabbitmq死信队列详解与使用

先从概念解释上搞清楚这个定义&#xff0c;死信&#xff0c;顾名思义就是无法被消费的消息&#xff0c;字面意思可以这样理解&#xff0c;一般来说&#xff0c;producer将消息投递到broker或者直接到queue里了&#xff0c;consumer从queue取出消息进行消费&#xff0c;但某些时…

使用ASP.NET Core 3.x 构建 RESTful API - 3.2 开始建立Controller和Action

Demo下面我们就来实践一下。打开之前的项目&#xff0c;并建立CompaniesController&#xff1a; 这里有6个地方比较关键&#xff0c;我们挨个看一下&#xff1a; RESTful API 或者其它Web API的Controller都应该继承于 ControllerBase 这个类&#xff08;点此查看详细的官方文档…

C++ 链表

线性表&#xff08;顺序表&#xff09;有两种存储方式&#xff1a;链式存储和顺式存储&#xff0c;顺式存储如数组&#xff0c;其内存连续分配&#xff0c;且是静态分配。链式存储&#xff0c;内存是不连续的&#xff0c;且是动态分配。前一个元素存储数据&#xff0c;后一个元…

波拉契尔数列 C++

题目&#xff1a;写一个函数&#xff0c;输入n, 求斐波那契数列的第n项。 分析&#xff1a;该题有两种实现方式递归或循环。当n比较大的时候f(n)结果也会比较大&#xff0c;故定义的时候可以采用long(int 也行)。递归会有大量的重复计算&#xff0c;而循环可以把f(n-1)和f(n-2)…

Deepin 下 使用 Rider 开发 .NET Core

国产的 Deepin 不错&#xff0c;安利一下。Deepin 用了也有一两年&#xff0c;也只是玩玩&#xff0c;没用在开发上面。后来 Win10 不太清真了&#xff0c;就想着能不能到 Deepin下撸码。要搞开发&#xff0c;首先少不了 IDE&#xff0c;VS2019 用不来&#xff0c;Vs Code 太复…

[视频演示].NET Core开发的iNeuOS物联网平台,实现从设备PLC、云平台、移动APP数据链路闭环...

此次我们团队人员对iNeuOS进行了全面升级&#xff0c;主要升级内容包括&#xff1a;&#xff08;1&#xff09; 设备容器增加设备驱动&#xff0c;包括&#xff1a;西门子&#xff08;S7-200smart、S7-300、S7-400、S7-1200、S7-1500&#xff09;、三菱&#xff08;FxSerial…

选择开源项目什么最重要?

开发人员在决定是否使用某个开源项目时考虑到的最重要事项是什么&#xff1f;代码质量&#xff1f;安全性&#xff1f;好的文档&#xff1f;上述因素都很重要&#xff0c;但根据 Tidelift 和 The New Stack 的联合调查&#xff0c;控制着开源项目的开源许可证才是最需要考量的因…

居然不知道和的区别?

前言那年刚找工作那会&#xff0c;就碰到过这么一个简单的题目“&和&&的区别” 那时知识面窄&#xff0c;大概也就知道1.都是作为逻辑与的运算符。2.&&具有短路功能&#xff0c;计算出前者false&#xff0c;就不需计算后者的true or false。后来在微信群里…

【DevOps进行时】自动化测试之单元测试

在DevOps建设中&#xff0c;主流的测试分层体系可以分为单元测试、接口测试和界面测试。Google曾提出一个经验法则&#xff1a;70%的小型测试&#xff0c;20%的中型测试&#xff0c;10%大型测试。当然&#xff0c;这个比例不是确定的&#xff0c;不同类型的项目&#xff0c;测试…

Zongsoft.Data 发布公告

很高兴我们的 ORM 数据访问框架(Zongsoft.Data)在历经两个 SaaS 产品的应用之后&#xff0c;今天正式宣布对外推广。它是一个类 GraphQL 风格的 ORM(Object/Relational Mapping) 数据访问框架。又一个轮子&#xff1f;在很长时间里&#xff0c;.NET 阵营似乎一直缺乏一个被普遍…

使用 .NET Core模板引擎创建自定义的模板和项目

本文要点.NET CLI 包含了一个模板引擎&#xff0c;它可以直接利用命令行创建新项目和项目项。这就是“dotnet new”命令。默认模板集涵盖了默认控制台和基于 ASP.NET 的应用程序以及测试项目所需的基本项目和文件类型。自定义模板可以创建更加有趣或定制化的项目和项目项&#…

.NET Core前后端分离快速开发框架(Core.3.0+AntdVue)

时间真快&#xff0c;转眼今年又要过去了。回想今年&#xff0c;依次开源发布了Colder.Fx.Net.AdminLTE(254Star)、Colder.Fx.Core.AdminLTE(335Star)、DotNettySocket(82Star)、IdHelper(47Star)&#xff0c;这些框架及组件都是本着以实际出发&#xff0c;实事求是的态度&…

.Net开发3年,应聘大厂惨遭淘汰,如何翻身打脸面试官?

(设计师忘记了&#xff0c;这里还有个双十一福利课&#xff0c;还能1元秒杀&#xff01;)

面对金九银十铜十一你真的准备好了吗?

作者&#xff1a;回首笑人间&#xff0c;高级Java工程师一枚&#xff0c;热爱研究开源技术&#xff0c;架构师社区合伙人&#xff01;前言&#xff1a;又是一年一度的金九银十跳槽季&#xff0c;回首在经历了半个月的求职奔波后&#xff0c;终于又能安稳的静下心来敲代码了&…

深入理解二叉搜索树

什么是二叉搜索树&#xff1f; 顾名思义&#xff0c;一颗二叉搜索树是基于二叉树来组织的&#xff0c;它包括许多动态集合操作&#xff08;Search&#xff0c;MiniNum, MaxiNum, Prodecessor, Successor, Insert 和Delete等&#xff09;。二叉搜索树上的基本操作所花费的时间与…

深入理解堆(最大堆,最小堆及堆排序)

基本概念&#xff1a; 1、完全二叉树&#xff1a;若二叉树的深度为h&#xff0c;则除第h层外&#xff0c;其他层的结点全部达到最大值&#xff0c;且第h层的所有结点都集中在左子树。 2、满二叉树&#xff1a;满二叉树是一种特殊的的完全二叉树&#xff0c;所有层的结点都是最…

王炸吐血整理60个Redis面试题,全网最全了

1.Redis 是一个基于内存的高性能key-value数据库。 2.Redis相比memcached有哪些优势&#xff1a; memcached所有的值均是简单的字符串&#xff0c;redis作为其替代者&#xff0c;支持更为丰富的数据类型redis的速度比memcached快很多redis可以持久化其数据3.Redis是单线程 redi…

H.266 参考软件VTM下载和安装

1、下载安装cmake &#xff0c;下载地址https://cmake.org/。 安装后打开控制面板-系统-高级系统设置-环境变量-PATH-编辑-输入cmake.exe的路径即可。 2、如果你之前&#xff08;HEVC&#xff09;时已经下载好了SVN&#xff0c;直接在桌面点击右键SVNcheckout&#xff0c;出来…