阅读源码学设计模式-单例模式
2019-11-14 13:11 李明成 阅读(393) 评论(0) 编辑 收藏 举报有些编码套路是公认的,大家都参照其编写符合可观赏性的代码,那就是设计模式
现在.NETcore 默认提供了DI功能,那我想设计一个全局的引擎类,进行注入服务、解析服务、配置中间件。并且要求该引擎类全局唯一,其他地方不能进行实例化。那单例模式就派上用场了。 单例模式官方定义:
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类成为单例类,它提供全局访问的方法。
伪代码实现需求
public class AAEngine { private static AAEngine aAEngine = null; private AAEngine() { } //获取实例 public static AAEngine GetInstance() { if (aAEngine == null) { aAEngine = new AAEngine(); } return aAEngine; } //添加服务到容器 public void ConfigureService() { Console.WriteLine("添加服务到容器"); } //添加中间件到请求管道 public void ConfigureRequestPipeline() { Console.WriteLine("添加中间件到请求管道"); } //解析服务 public void Resolve<T>() where T : class { Console.WriteLine("解析服务"); } }
在Main函数中调用
//单例模式 static void runSingleton() { var aAEngine = AAEngine.GetInstance(); aAEngine.ConfigureService(); aAEngine.ConfigureRequestPipeline(); Console.WriteLine("Oh yeah 单例模式! "); }
输出
小结:从实例代码中我们看到构造函数设置了级别为private,这样可以防止外部进行new实例化,外部可以通过GetInstance方法获取实例对象。实例代码其实是有点瑕疵的在多线程的情况下,会违背单例的初衷,我们下面进行如何解决这个问题。
突然脑海中闪现出曾经的面试场景,饿汉式单例和懒汉式单例,或许对问题有所有帮助;
饿汉式单例
饿汉试单例是在类加载的时候就已经创建了对象。代码如下
public class AAEngine1 { private static AAEngine1 aAEngine = new AAEngine1(); private AAEngine1() { } //获取实例 public static AAEngine1 GetInstance() { return aAEngine; } //添加服务到容器 public void ConfigureService() { Console.WriteLine("添加服务到容器"); } //添加中间件到请求管道 public void ConfigureRequestPipeline() { Console.WriteLine("添加中间件到请求管道"); } //解析服务 public void Resolve<T>() where T : class { Console.WriteLine("解析服务"); } }
小结:在类被加载时,静态变量aAEngine会被初始化,AAEngine1类的唯一实例将会创建,则多线程并发的场景下,也可确保单例对象的唯一性; 那什么是懒汉式单例呢?其实最上面的AAEngine就是懒汉式单例,在多线程并发的场景下懒汉式单例有问题,如何解决 答案是通过锁的方式。
懒汉式单例+线程锁
懒汉式单例有延迟Lazy的思想,只有在需要的时候才去加载实例。在多线程并发的场景下我们使用双重检查锁定(Double-Check Locking)。完成代码如下:
private static AAEngine aAEngine = null; private static object lockObj=new object(); private AAEngine() { } //获取实例 public static AAEngine GetInstance() { //第一重验证 if (aAEngine == null) { lock (lockObj) { //第二重验证 if (aAEngine==null) { aAEngine = new AAEngine(); } } } return aAEngine; }
单例模式在开源Nop项目中实践
为了配合你没有阅读过Nop项目源码,我会把涉及到单例的几个类源码贴出来。主要设计到3个类Singleton、IEngine、EngineContext。
Singleton类
public class Singleton<T> : BaseSingleton { private static T instance; /// <summary> /// The singleton instance for the specified type T. Only one instance (at the time) of this object for each type of T. /// </summary> public static T Instance { get => instance; set { instance = value; AllSingletons[typeof(T)] = value; } } }
IEngine类
public interface IEngine { /// <summary> /// 添加配置服务 Add and configure services /// </summary> /// <param name="services">Collection of service descriptors</param> /// <param name="configuration">Configuration of the application</param> /// <param name="nopConfig">Nop configuration parameters</param> /// <returns>Service provider</returns> IServiceProvider ConfigureServices(IServiceCollection services, IConfiguration configuration, NopConfig nopConfig); /// <summary> /// 配置请求管道 Configure HTTP request pipeline /// </summary> /// <param name="application">Builder for configuring an application's request pipeline</param> void ConfigureRequestPipeline(IApplicationBuilder application); /// <summary> /// 解析服务 Resolve dependency /// </summary> /// <typeparam name="T">Type of resolved service</typeparam> /// <returns>Resolved service</returns> T Resolve<T>() where T : class; /// <summary> /// 解析服务 Resolve dependency /// </summary> /// <param name="type">Type of resolved service</param> /// <returns>Resolved service</returns> object Resolve(Type type); /// <summary> /// 解析所有服务Resolve dependencies /// </summary> /// <typeparam name="T">Type of resolved services</typeparam> /// <returns>Collection of resolved services</returns> IEnumerable<T> ResolveAll<T>(); /// <summary> /// Resolve unregistered service /// </summary> /// <param name="type">Type of service</param> /// <returns>Resolved service</returns> object ResolveUnregistered(Type type); }
EngineContext 引擎上下文类
#region Methods /// <summary> /// Create a static instance of the Nop engine. /// </summary> [MethodImpl(MethodImplOptions.Synchronized)] public static IEngine Create() { //create NopEngine as engine return Singleton<IEngine>.Instance ?? (Singleton<IEngine>.Instance = new NopEngine()); } /// <summary> /// Sets the static engine instance to the supplied engine. Use this method to supply your own engine implementation. /// </summary> /// <param name="engine">The engine to use.</param> /// <remarks>Only use this method if you know what you're doing.</remarks> public static void Replace(IEngine engine) { Singleton<IEngine>.Instance = engine; } #endregion #region Properties /// <summary> /// Gets the singleton Nop engine used to access Nop services. /// </summary> public static IEngine Current { get { if (Singleton<IEngine>.Instance == null) { Create(); } return Singleton<IEngine>.Instance; } } #endregion }
从nop的源码中我们发现,他使用的懒汉式单例(含双重检查锁定),外部访问IEngine实例是通过EngineContext上下文来访问的。在创建IEngine实例方法create()时,使用时的 [MethodImpl(MethodImplOptions.Synchronized)]特性,表示create方法只能由一个线程执行,类似lock锁。
如何使用,代码如下
//create engine and configure service provider var engine = EngineContext.Create(); var serviceProvider = engine.ConfigureServices(services, configuration, nopConfig); 。。。。。。。。。。。。 EngineContext.Current.Resolve<IScheduleTaskService>();
nop封装的优秀的代码,也收录到我的开源项目中了,喜欢它可以star下 https://github.com/ChengLab/AAFrameWork