.NET 雲原生架構師訓練營(模板方法 && 建造者)--學習筆記

MingsonZheng發表於2022-01-07

目錄

  • 模板方法
  • 原始碼
  • 建造者

模板方法

定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟

原始碼

https://github.com/dotnet/aspnetcore/

在目錄 aspnetcore\src\Mvc\Mvc.Core\src\Infrastructure 下有一個 ControllerActionInvoker,它繼承自 ResourceInvoker

internal class ControllerActionInvoker : ResourceInvoker, IActionInvoker

在 ResourceInvoker 中定義了一些演算法的骨架,在 InvokeAsync 方法中對一些方法進行了組裝

public virtual Task InvokeAsync()
{
    ...
    task = InvokeFilterPipelineAsync();
    ...
    return ReleaseResourcesCore(scope).AsTask();
    ...
}

還有一些抽象方法需要在子類 ControllerActionInvoker 中實現

/// <summary>
/// In derived types, releases resources such as controller, model, or page instances created as
/// part of invoking the inner pipeline.
/// </summary>
protected abstract ValueTask ReleaseResources();

protected abstract Task InvokeInnerFilterAsync();

這裡就是模板方法的一個應用,通過抽象類和一個子類來實現

子類沒有 InvokeAsync 方法,它在頂層完成了封裝,對多個方法進行呼叫,同時提供一些中間聯合的方法

從 MapControllers 方法的角度看,呼叫了 ControllerEndpointRouteBuilderExtensions

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

ControllerEndpointRouteBuilderExtensions 這個類會告訴你整個註冊的過程發生了什麼

首先,它接收了一個 IEndpointRouteBuilder

public static ControllerActionEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints)
{
    ...
    
    EnsureControllerServices(endpoints);

    return GetOrCreateDataSource(endpoints).DefaultBuilder;
}

在 EnsureControllerServices 中把所有的服務獲取進來

var marker = endpoints.ServiceProvider.GetService<MvcMarkerService>();

MvcMarkerService 需要先註冊,獲取 DataSources,然後註冊

private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
{
    var dataSource = endpoints.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault();
    if (dataSource == null)
    {
        var orderProvider = endpoints.ServiceProvider.GetRequiredService<OrderedEndpointsSequenceProviderCache>();
        var factory = endpoints.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSourceFactory>();
        dataSource = factory.Create(orderProvider.GetOrCreateOrderedEndpointsSequenceProvider(endpoints));
        endpoints.DataSources.Add(dataSource);
    }

    return dataSource;
}

在 ControllerActionEndpointDataSource 中遍歷 actions

for (var i = 0; i < actions.Count; i++)
{
    if (actions[i] is ControllerActionDescriptor action)
    {
        _endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);

這些 actions 來自於基類 ActionEndpointDataSourceBase

public ActionEndpointDataSourceBase(IActionDescriptorCollectionProvider actions)
{
    _actions = actions;

    Conventions = new List<Action<EndpointBuilder>>();
}

actions 通過 CreateEndpoints 繫結到 RequestDelegate

protected override List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions)

CreateEndpoints 中有一個 AddEndpoints 方法

_endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);

在 AddEndpoints 方法中將一個 action 轉換為一個 endpoint

var builder = new InertEndpointBuilder()
{
    DisplayName = action.DisplayName,
    RequestDelegate = _requestDelegate,
};
AddActionDataToBuilder(
    builder,
    routeNames,
    action,
    routeName: null,
    dataTokens: null,
    suppressLinkGeneration: false,
    suppressPathMatching: false,
    conventions,
    Array.Empty<Action<EndpointBuilder>>());
endpoints.Add(builder.Build());

接著看一下 _requestDelegate

_requestDelegate = CreateRequestDelegate();

這裡才是真正執行每個 web api 請求的入口

private static RequestDelegate CreateRequestDelegate()
{
    // We don't want to close over the Invoker Factory in ActionEndpointFactory as
    // that creates cycles in DI. Since we're creating this delegate at startup time
    // we don't want to create all of the things we use at runtime until the action
    // actually matches.
    //
    // The request delegate is already a closure here because we close over
    // the action descriptor.
    IActionInvokerFactory? invokerFactory = null;

    return (context) =>
    {
        var endpoint = context.GetEndpoint()!;
        var dataTokens = endpoint.Metadata.GetMetadata<IDataTokensMetadata>();

        var routeData = new RouteData();
        routeData.PushState(router: null, context.Request.RouteValues, new RouteValueDictionary(dataTokens?.DataTokens));

        // Don't close over the ActionDescriptor, that's not valid for pages.
        var action = endpoint.Metadata.GetMetadata<ActionDescriptor>()!;
        var actionContext = new ActionContext(context, routeData, action);

        if (invokerFactory == null)
        {
            invokerFactory = context.RequestServices.GetRequiredService<IActionInvokerFactory>();
        }

        var invoker = invokerFactory.CreateInvoker(actionContext);
        return invoker!.InvokeAsync();
    };
}

首先從 context 獲取 endpoint,接著從 endpoint 得到 ActionDescriptor,再將它封裝成一個 ActionContext

通過 invokerFactory 建立一個 invoker,最後呼叫 InvokeAsync,所以整個執行過程是一個委託,在執行 MapControllers 的時候已經將委託掛到整個執行的 endpoint

每個路由的 endpoint 最後都是指向同一個地方,全部指向同一個 Delegate,只不過這個 Delegate 從 endpoint 的 Metadata 中拿到的 action 的定義,包括 controller, method, parameter

最後通過 invoker 的形式呼叫,所以才用到了 ResourceInvoker, PageActionInvoker, 和 ControllerActionInvoker 三種方式,發揮了模板方法作用

建造者

它是將一個複雜的物件分解為多個簡單的物件,然後一步一步構建而成

它將變與不變相分離,即產品的組成部分是不變的,但每一部分是可以靈活選擇的

建造者和模板方法有點類似,一個屬於行為型的設計模式,一個屬於建立型的設計模式

模板方法強調的是行為上面的分解,建造者更加關注建立物件的分解

兩者都是基於一個抽象的類提供抽象方法交給具體的類實現,程式碼類似,意義不同

課程連結

https://appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2

知識共享許可協議

本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含連結: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。

如有任何疑問,請與我聯絡 (MingsonZheng@outlook.com) 。

相關文章