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

.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 中的各種 BuilderHostBuilder/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 接口,內部實現了深複製
來看 StackClone 方法實現:

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 的源碼

以上均是個人理解,如果有錯誤還望指出,十分感謝,歡迎補充更多設計模式應用的源碼實例

Reference

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章