1. 程式人生 > >.net core 中的經典設計模式的應用

.net core 中的經典設計模式的應用

# .net core 中的經典設計模式的應用 ## Intro 前段時間我們介紹了23種設計模式,今天來分享一下 .net core 原始碼中我覺得比較典型的設計模式的應用 ## 例項 ### 責任鏈模式 asp.net core 中介軟體的設計就是責任鏈模式的應用和變形, 每個中介軟體根據需要處理請求,並且可以根據請求資訊自己決定是否傳遞給下一個中介軟體,我也受此啟發,封裝了一個 `PipelineBuilder` 可以輕鬆構建中介軟體模式程式碼,可以參考這篇文章 中介軟體示例: ``` csharp app.UseStaticFiles(); app.UseResponseCaching(); app.UseResponseCompression(); app.UseRouting(); app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapControllerRoute(name: "areaRoute", "{area:exists}/{controller=Home}/{action=Index}"); endpoints.MapDefaultControllerRoute(); }); ``` `PipelineBuilder` 實際示例: ``` csharp var requestContext = new RequestContext() { RequesterName = "Kangkang", Hour = 12, }; var builder = PipelineBuilder.Create(context => { Console.WriteLine($"{context.RequesterName} {context.Hour}h apply failed"); }) .Use((context, next) => { if (context.Hour <= 2) { Console.WriteLine("pass 1"); } else { next(); } }) .Use((context, next) => { if (context.Hour <= 4) { Console.WriteLine("pass 2"); } else { next(); } }) .Use((context, next) => { if (context.Hour <= 6) { Console.WriteLine("pass 3"); } else { next(); } }) ; var requestPipeline = builder.Build(); foreach (var i in Enumerable.Range(1, 8)) { Console.WriteLine(); Console.WriteLine($"--------- h:{i} apply Pipeline------------------"); requestContext.Hour = i; requestPipeline.Invoke(requestContext); Console.WriteLine("----------------------------"); Console.WriteLine(); } ``` ### 建造者模式 asp.net core 中的各種 `Builder`, `HostBuilder`/`ConfigurationBuilder` 等,這些 `Builder` 大多既是 Builder 又是 Director,Builder 本身知道如何構建最終的 `Product`(`Host`/`Configuration`) ``` csharp var host = new HostBuilder() .ConfigureAppConfiguration(builder =>
{ // 註冊配置 builder .AddInMemoryCollection(new Dictionary() { {"UserName", "Alice"} }) .AddJsonFile("appsettings.json") ; }) .ConfigureServices((context, services) => { // 註冊自定義服務 services.AddSingleton(); services.AddTransient(); if (context.Configuration.GetAppSetting("XxxEnabled")) { services.AddSingleton(); } }) .Build() ; ``` ### 工廠模式 依賴注入框架中有著大量的工廠模式的程式碼,註冊服務的時候我們可以通過一個工廠方法委託來獲取服務例項, 依賴注入的本質就是將物件的建立交給 IOC 容器來處理,所以其實 IOC 容器本質就是一個工廠,從 IOC 中獲取服務例項的過程就是工廠建立物件的過程,只是會根據服務的生命週期來決定是建立新物件還是返回已有物件。 ``` csharp services.AddSingleton(sp => new Svc2(sp.GetRequiredService(), "xx")); ``` ### 單例模式 在 dotnet 中有一個 `TimeQueue` 的型別,純正的餓漢模式的單例模式程式碼 ``` csharp class TimerQueue { #region singleton pattern implementation // The one-and-only TimerQueue for the AppDomain. static TimerQueue s_queue = new TimerQueue(); public static TimerQueue Instance { get { return s_queue; } } private TimerQueue() { // empty private constructor to ensure we remain a singleton. } #endregion // ... } ```
在 dotnet 原始碼中還有一些懶漢式的單例模式 使用 `Interlocked` 原子操作 ``` csharp internal class SimpleEventTypes : TraceLoggingEventTypes { private static SimpleEventTypes instance; internal readonly TraceLoggingTypeInfo typeInfo; private SimpleEventTypes(TraceLoggingTypeInfo typeInfo) : base( typeInfo.Name, typeInfo.Tags, new TraceLoggingTypeInfo[] { typeInfo }) { this.typeInfo = typeInfo; } public static SimpleEventTypes Instance { get { return instance ?? InitInstance(); } } private static SimpleEventTypes InitInstance() { var newInstance = new SimpleEventTypes(TraceLoggingTypeInfo.Instance); Interlocked.CompareExchange(ref instance, newInstance, null); return instance; } } ``` 另外一個示例,需要注意,下面這種方式不能嚴格的保證只會產生一個例項,在併發較高的情況下可能不是同一個例項,這也可以算是工廠模式的一個示例 ``` csharp static internal class ConfigurationManagerHelperFactory { private const string ConfigurationManagerHelperTypeString = "System.Configuration.Internal.ConfigurationManagerHelper, " + AssemblyRef.System; static private volatile IConfigurationManagerHelper s_instance; static internal IConfigurationManagerHelper Instance { get { if (s_instance == null) { s_instance = CreateConfigurationManagerHelper(); } return s_instance; } } [ReflectionPermission(SecurityAction.Assert, Flags = ReflectionPermissionFlag.MemberAccess)] [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Hard-coded to create an instance of a specific type.")] private static IConfigurationManagerHelper CreateConfigurationManagerHelper() { return TypeUtil.CreateInstance(ConfigurationManagerHelperTypeString); } } ``` ### 原型模式 dotnet 中有兩個資料結構 `Stack`/`Queue` 這兩個資料都實現了 `ICloneable` 介面,內部實現了深複製 來看 `Stack` 的 `Clone` 方法實現: ``` csharp public virtual Object Clone() { Contract.Ensures(Contract.Result() != null); Stack s = new Stack(_size); s._size = _size; Array.Copy(_array, 0, s._array, 0, _size); s._version = _version; return s; } ``` 詳細可以參考:
### 享元模式 string intern(字串池),以及 `Array.Empty()`/`Array.Empty()` 等 ### 策略模式 asp.net core 中的認證和授權,我覺得就是策略模式的應用,在使用 `[Authorize]` 的時候會使用預設的 policy,也可以指定要使用的策略 `[Authorize("Policy1")]` 這樣就會使用另外一種策略 `Policy1`,policy 還是比較簡單的 policy 是用來根據使用者的認證資訊來控制授權訪問的,而認證則是根據當前上下文(請求上下文、執行緒上下文、環境上下文等)的資訊進行認證從而獲取使用者資訊的過程 而不同的認證模式(Cookie/JWT/自定義Token等)其實是不同的處理方法,也就是策略模式中不同的演算法實現,指定哪種認證模式,就是使用哪種演算法實現來獲取使用者資訊 ### 觀察者模式 常使用事件(event)進行解耦,外部程式碼通過訂閱事件來解耦,實現對內部狀態的觀察 在 `Process` 類中有很多事件,可以用來捕獲另一個程序中的輸出,錯誤等 ``` csharp public event DataReceivedEventHandler OutputDataReceived; public event DataReceivedEventHandler ErrorDataReceived; ``` 通常這兩個事件我們就可以獲取到另外一個程序中的輸出資訊,除此之外還有很多的類在使用事件,相信你也用過很多 ### 組合模式 WPF、WinForm 中都有控制元件的概念,這些控制元件的設計屬於是組合模式的應用,所有的控制元件都會繼承於某一個共同的基類, 使得單個物件和組合物件都可以看作是他們共同的基類物件 ### 迭代器模式 c# 中定義了迭代器模式,原始定義: ``` csharp // 聚集抽象 public interface IEnumerable { /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. IEnumerator GetEnumerator(); } // 迭代器抽象 public interface IEnumerator { /// Advances the enumerator to the next element of the collection. /// /// if the enumerator was successfully advanced to the next element; if the enumerator has passed the end of the collection. /// The collection was modified after the enumerator was created. bool MoveNext(); /// Gets the element in the collection at the current position of the enumerator. /// The element in the collection at the current position of the enumerator. object Current { get; } /// Sets the enumerator to its initial position, which is before the first element in the collection. /// The collection was modified after the enumerator was created. void Reset(); } ``` Array 和 List 各自實現了自己的迭代器,感興趣可以去看下原始碼 ## More .net core 中的設計模式應用還有很多,不僅上面提到的這幾個模式,也不僅僅是我所提到的這幾個地方 > 上面有一些示例是直接用的 dotnet framework 中的原始碼,因為有很多程式碼都是類似的,用的 https://referencesource.microsoft.com 的原始碼 以上均是個人理解,如果有錯誤還望指出,十分感謝,歡迎補充更多設計模式應用的原始碼例項 ## Reference - - - - -