阅读源码学设计模式-单例模式
有些編碼套路是公認的,大家都參照其編寫符合可觀賞性的代碼,那就是設計模式
現在.NETcore 默認提供了DI功能,那我想設計一個全局的引擎類,進行注入服務、解析服務、配置中間件。并且要求該引擎類全局唯一,其他地方不能進行實例化。那單例模式就派上用場了。單例模式官方定義:
確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例,這個類成為單例類,它提供全局訪問的方法。偽代碼實現需求
}
在Main函數中調用
輸出
小結:從實例代碼中我們看到構造函數設置了級別為private,這樣可以防止外部進行new實例化,外部可以通過GetInstance方法獲取實例對象。實例代碼其實是有點瑕疵的在多線程的情況下,會違背單例的初衷,我們下面進行如何解決這個問題。
突然腦海中閃現出曾經的面試場景,餓漢式單例和懶漢式單例,或許對問題有所有幫助;
餓漢式單例
餓漢試單例是在類加載的時候就已經創建了對象。代碼如下
//添加服務到容器 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)。完成代碼如下:
單例模式在開源Nop項目中實踐
為了配合你沒有閱讀過Nop項目源碼,我會把涉及到單例的幾個類源碼貼出來。主要設計到3個類Singleton、IEngine、EngineContext。
Singleton類
/// <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類
/// <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 引擎上下文類
/// <summary> /// Create a static instance of the Nop engine. /// </summary> [] 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; }
/// <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; } }
}
從nop的源碼中我們發現,他使用的懶漢式單例(含雙重檢查鎖定),外部訪問IEngine實例是通過EngineContext上下文來訪問的。在創建IEngine實例方法create()時,使用時的 [MethodImpl(MethodImplOptions.Synchronized)]特性,表示create方法只能由一個線程執行,類似lock鎖。
如何使用,代碼如下
nop封裝的優秀的代碼,也收錄到我的開源項目中了,喜歡可以star下?https://github.com/ChengLab/AAFrameWork
總結
以上是生活随笔為你收集整理的阅读源码学设计模式-单例模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET手撸绘制TypeScript类图
- 下一篇: 我终于知道post和get的区别