catalogue
- Template method
- Source code
- builder
Template method
Define the skeleton of an algorithm in operation, and delay some steps to subclasses, so that subclasses can redefine some specific steps of an algorithm without changing the structure of an algorithm
Source code
https://github.com/dotnet/aspnetcore/
In the directory aspnetcore \ SRC \ MVC \ MVC There is a ControllerActionInvoker under core \ SRC \ infrastructure, which inherits from ResourceInvoker
internal class ControllerActionInvoker : ResourceInvoker, IActionInvoker
Some algorithm skeletons are defined in ResourceInvoker, and some methods are assembled in InvokeAsync method
public virtual Task InvokeAsync() { ... task = InvokeFilterPipelineAsync(); ... return ReleaseResourcesCore(scope).AsTask(); ... }
There are also some abstract methods that need to be implemented in the subclass 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();
Here is an application of template method, which is implemented through abstract classes and subclasses
The subclass has no InvokeAsync method. It completes the encapsulation at the top level, calls multiple methods, and provides some intermediate joint methods
From the perspective of MapControllers method, controllerendpointrotebuilderextensions is called
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
The controllerendpointrotebuilderextensions class will tell you what happened during the whole registration process
First, it receives an iendpoint routebuilder
public static ControllerActionEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints) { ... EnsureControllerServices(endpoints); return GetOrCreateDataSource(endpoints).DefaultBuilder; }
Get all services in the insurance controller services
var marker = endpoints.ServiceProvider.GetService<MvcMarkerService>();
MvcMarkerService needs to register first, obtain DataSources, and then register
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; }
Traverse actions in ControllerActionEndpointDataSource
for (var i = 0; i < actions.Count; i++) { if (actions[i] is ControllerActionDescriptor action) { _endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);
These actions come from the base class ActionEndpointDataSourceBase
public ActionEndpointDataSourceBase(IActionDescriptorCollectionProvider actions) { _actions = actions; Conventions = new List<Action<EndpointBuilder>>(); }
actions is bound to RequestDelegate through CreateEndpoints
protected override List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions)
There is an AddEndpoints method in CreateEndpoints
_endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);
Convert an action to an endpoint in the AddEndpoints method
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());
Take a look_ requestDelegate
_requestDelegate = CreateRequestDelegate();
This is the entry point to actually execute every web api request
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(); }; }
First, get the endpoint from the context, then get the ActionDescriptor from the endpoint, and then encapsulate it into an ActionContext
Create a invoker through invokerFactory, and finally call InvokeAsync, so the whole execution process is a delegate. When MapControllers is executed, the delegate is linked to the whole execution of endpoint.
The endpoint of each route finally points to the same place and all points to the same Delegate, but the definition of action obtained by this Delegate from the endpoint Metadata includes controller, method and parameter
Finally, it is called in the form of invoker, so three methods, ResourceInvoker, PageActionInvoker and ControllerActionInvoker, are used to play the role of template method
builder
It decomposes a complex object into several simple objects, and then builds them step by step
It separates change from invariance, that is, the components of the product are invariable, but each part can be flexibly selected
The builder and template methods are somewhat similar. One belongs to behavioral design pattern and the other belongs to creative design pattern
The template method emphasizes the decomposition of behavior, and the builder pays more attention to the decomposition of creating objects
Both are based on an abstract class and provide abstract methods to specific class implementations. The code is similar and the meaning is different
Course links
https://appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2
This work adopts Knowledge sharing Attribution - non-commercial use - sharing in the same way 4.0 international license agreement License.
Welcome to reprint, use and republish, but be sure to keep the signature Zheng Ziming (including link: http://www.cnblogs.com/MingsonZheng/ ), shall not be used for commercial purposes, and the works modified based on this article must be distributed under the same license.
If you have any questions, please contact me( MingsonZheng@outlook.com) .