.net core 中的經典設計模式的應用
Intro
前段時間我們介紹了23種設計模式,今天來分享一下 .net core 源碼中我覺得比較典型的設計模式的應用
實例
責任鏈模式
asp.net core 中間件的設計就是責任鏈模式的應用和變形,
每個中間件根據需要處理請求,並且可以根據請求信息自己決定是否傳遞給下一個中間件,我也受此啟發,封裝了一個 PipelineBuilder
可以輕松構建中間件模式代碼,可以參考這篇文章 https://www.cnblogs.com/weihanli/p/12700006.html
中間件示例:
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
實際示例:
var requestContext = new RequestContext()
{
RequesterName = "Kangkang",
Hour = 12,
};
var builder = PipelineBuilder.Create<RequestContext>(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
)
var host = new HostBuilder()
.ConfigureAppConfiguration(builder =>
{
// 注冊配置
builder
.AddInMemoryCollection(new Dictionary<string, string>()
{
{"UserName", "Alice"}
})
.AddJsonFile("appsettings.json")
;
})
.ConfigureServices((context, services) =>
{
// 注冊自定義服務
services.AddSingleton<IIdGenerator, GuidIdGenerator>();
services.AddTransient<IService, Service>();
if (context.Configuration.GetAppSetting<bool>("XxxEnabled"))
{
services.AddSingleton<IUserIdProvider, EnvironmentUserIdProvider>();
}
})
.Build()
;
工廠模式
依賴注入框架中有着大量的工廠模式的代碼,注冊服務的時候我們可以通過一個工廠方法委托來獲取服務實例,
依賴注入的本質就是將對象的創建交給 IOC 容器來處理,所以其實 IOC 容器本質就是一個工廠,從 IOC 中獲取服務實例的過程就是工廠創建對象的過程,只是會根據服務的生命周期來決定是創建新對象還是返回已有對象。
services.AddSingleton(sp => new Svc2(sp.GetRequiredService<ISvc1>(), "xx"));
單例模式
在 dotnet 中有一個 TimeQueue
的類型,純正的餓漢模式的單例模式代碼
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
// ...
}
https://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs,49
在 dotnet 源碼中還有一些懶漢式的單例模式
使用 Interlocked
原子操作
internal class SimpleEventTypes<T>
: TraceLoggingEventTypes
{
private static SimpleEventTypes<T> instance;
internal readonly TraceLoggingTypeInfo<T> typeInfo;
private SimpleEventTypes(TraceLoggingTypeInfo<T> typeInfo)
: base(
typeInfo.Name,
typeInfo.Tags,
new TraceLoggingTypeInfo[] { typeInfo })
{
this.typeInfo = typeInfo;
}
public static SimpleEventTypes<T> Instance
{
get { return instance ?? InitInstance(); }
}
private static SimpleEventTypes<T> InitInstance()
{
var newInstance = new SimpleEventTypes<T>(TraceLoggingTypeInfo<T>.Instance);
Interlocked.CompareExchange(ref instance, newInstance, null);
return instance;
}
}
另外一個示例,需要注意,下面這種方式不能嚴格的保證只會產生一個實例,在並發較高的情況下可能不是同一個實例,這也可以算是工廠模式的一個示例
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<IConfigurationManagerHelper>(ConfigurationManagerHelperTypeString);
}
}
原型模式
dotnet 中有兩個數據結構 Stack
/Queue
這兩個數據都實現了 ICloneable
接口,內部實現了深復制
來看 Stack
的 Clone
方法實現:
public virtual Object Clone()
{
Contract.Ensures(Contract.Result<Object>() != null);
Stack s = new Stack(_size);
s._size = _size;
Array.Copy(_array, 0, s._array, 0, _size);
s._version = _version;
return s;
}
詳細可以參考: https://referencesource.microsoft.com/#mscorlib/system/collections/stack.cs,6acda10c5f8b128e
享元模式
string intern(字符串池),以及 Array.Empty<int>()
/Array.Empty<string>()
等
策略模式
asp.net core 中的認證和授權,我覺得就是策略模式的應用,在使用 [Authorize]
的時候會使用默認的 policy,也可以指定要使用的策略 [Authorize("Policy1")]
這樣就會使用另外一種策略 Policy1
,policy 還是比較簡單的
policy 是用來根據用戶的認證信息來控制授權訪問的,而認證則是根據當前上下文(請求上下文、線程上下文、環境上下文等)的信息進行認證從而獲取用戶信息的過程
而不同的認證模式(Cookie/JWT/自定義Token等)其實是不同的處理方法,也就是策略模式中不同的算法實現,指定哪種認證模式,就是使用哪種算法實現來獲取用戶信息
觀察者模式
常使用事件(event)進行解耦,外部代碼通過訂閱事件來解耦,實現對內部狀態的觀察
在 Process
類中有很多事件,可以用來捕獲另一個進程中的輸出,錯誤等
public event DataReceivedEventHandler OutputDataReceived;
public event DataReceivedEventHandler ErrorDataReceived;
通常這兩個事件我們就可以獲取到另外一個進程中的輸出信息,除此之外還有很多的類在使用事件,相信你也用過很多
組合模式
WPF、WinForm 中都有控件的概念,這些控件的設計屬於是組合模式的應用,所有的控件都會繼承於某一個共同的基類, 使得單個對象和組合對象都可以看作是他們共同的基類對象
迭代器模式
c# 中定義了迭代器模式,原始定義:
// 聚集抽象
public interface IEnumerable
{
/// <summary>Returns an enumerator that iterates through a collection.</summary>
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
IEnumerator GetEnumerator();
}
// 迭代器抽象
public interface IEnumerator
{
/// <summary>Advances the enumerator to the next element of the collection.</summary>
/// <returns>
/// <see langword="true" /> if the enumerator was successfully advanced to the next element; <see langword="false" /> if the enumerator has passed the end of the collection.</returns>
/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created.</exception>
bool MoveNext();
/// <summary>Gets the element in the collection at the current position of the enumerator.</summary>
/// <returns>The element in the collection at the current position of the enumerator.</returns>
object Current { get; }
/// <summary>Sets the enumerator to its initial position, which is before the first element in the collection.</summary>
/// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created.</exception>
void Reset();
}
Array 和 List 各自實現了自己的迭代器,感興趣可以去看下源碼
適配器模式
之前介紹的適配器模式中有提到 asp.net core 3.x 里引入了 ServiceProviderFactory
, 使得 .net core 可以更方便的集成第三方的依賴注入框架,這里使用了適配器模式通過 ServiceFactoryAdapter
來適配各種不同的第三方的依賴注入框架
More
.net core 中的設計模式應用還有很多,不僅上面提到的這幾個模式,也不僅僅是我所提到的這幾個地方
上面有一些示例是直接用的 dotnet framework 中的源碼,因為有很多代碼都是類似的,用的 https://referencesource.microsoft.com 的源碼
以上均是個人理解,如果有錯誤還望指出,十分感謝,歡迎補充更多設計模式應用的源碼實例