.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 各自實現了自己的迭代器,感興趣可以去看下原始碼
More
.net core 中的設計模式應用還有很多,不僅上面提到的這幾個模式,也不僅僅是我所提到的這幾個地方
上面有一些示例是直接用的 dotnet framework 中的原始碼,因為有很多程式碼都是類似的,用的 https://referencesource.microsoft.com 的原始碼
以上均是個人理解,如果有錯誤還望指出,十分感謝,歡迎補充更多設計模式應用的原始碼例項