代码改变世界

如何设计出和 ASP.NET Core 中 Middleware 一样的 API 方法?

2019-06-14 23:42  音乐让我说  阅读(435)  评论(0编辑  收藏  举报

由于笔者时间有限,无法写更多的说明文本,且主要是自己用来记录学习点滴,请谅解,下面直接贴代码了(代码中有一些说明):

01-不好的设计

代码:

using System;

namespace DesignSample
{
    public class TrTemplateContext { public string TrAttrPrefix { get; set; } }

    class Program
    {
        public static void Main(string[] args)
        {
            AppendTimeForTrTag(c => string.Format("{0}-id=\"{1}\"", c.TrAttrPrefix, "tr1"));
            //很显然,这是一个糟糕的设计,意味着每增加一个类似 AppendTimeForTrTag 的封装就
            //要增加很多类似于 AppendTimeForTrTag 的代码。参考 ASP.NET Core 中的 middleware
        }

        static void AppendTimeForTrTag(Func<TrTemplateContext, string> trTemplate)
        {/* 假设本方法来自于你们公司的A部门,通过封装,用于给<tr>标签固定附加 ng-time 属性 */
            Func<TrTemplateContext, string> trTimeTemplate = 
                c => string.Format("{0}-time=\"{1}\"", 
                        c.TrAttrPrefix,
                        DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            Func<TrTemplateContext, string> trAllTemplate = trTimeTemplate;
            if(trTemplate != null)
            {
                trAllTemplate = c => trTimeTemplate(c) + " " + trTemplate(c);
            }
            PrintTrTag(trAllTemplate);
        }
        
        static void PrintTrTag(Func<TrTemplateContext, string> trTemplate)
        {/* 假设本方法来自于ASP.NET Core内部。用于给<tr>标签附加一系列以 ng- 开头的属性 */
            string htmlTempl = "<tr {0}></tr>";
            string trInner = null;
            TrTemplateContext templContext = new TrTemplateContext { TrAttrPrefix = "ng" };
            if (trTemplate != null)
            {
                trInner = trTemplate(templContext);
            }
            string fullHtml = string.Format(htmlTempl, trInner);
            Console.WriteLine(fullHtml);
        }
    }
}

运行结果:

 

 

02-中间件设计(未提取公共代码)

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignSample
{
    public class TrAttrTemplateContext
    {
        public string TrAttrPrefix { get; set; }

        private StringBuilder trAttrBuilder = new StringBuilder();

        public void Add(string str)
        {
            if(trAttrBuilder.Length > 0)
            {
                trAttrBuilder.Append(" ");
            }
            trAttrBuilder.Append(str);
        }

        public string GetAllString()
        {
            return trAttrBuilder.ToString();
        }
    }
    public delegate void RequestDelegate(TrAttrTemplateContext builder);
    public class TrAttrTemplateBuilder
    {
        private readonly List<Func<RequestDelegate, RequestDelegate>> _middlewares
            = new List<Func<RequestDelegate, RequestDelegate>>();

        public TrAttrTemplateBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
        {
            _middlewares.Add(middleware);
            return this;
        }

        public RequestDelegate Build()
        {
            _middlewares.Reverse();
            RequestDelegate next = c => { };
            foreach (var middleware in _middlewares)
            {
                next = middleware(next);
            }
            return next;
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            PrintTrTag(app => 
                app.Use(AppendIdForTrTag) //给 tr 标签增加 ng-id 属性
                   .Use(AppendTimeForTrTag)  //给 tr 标签增加 ng-time 属性
            );
        }

        /* 假设本方法来自于你们公司的B部门,通过封装,用于给<tr>标签固定附加 ng-id 属性 */
        static RequestDelegate AppendIdForTrTag(RequestDelegate next) => context =>
        {
            context.Add($"{ context.TrAttrPrefix }-id=\"tr1\"");
            next(context);
        };

        /* 假设本方法来自于你们公司的A部门,通过封装,用于给<tr>标签固定附加 ng-time 属性 */
        static RequestDelegate AppendTimeForTrTag(RequestDelegate next) => context =>
        {
            context.Add($"{ context.TrAttrPrefix }-time=\"{ DateTime.Now.ToString() }\"");
            next(context);
        };

        /* 假设本方法来自于ASP.NET Core内部。用于给<tr>标签附加一系列以 ng- 开头的属性 */
        static void PrintTrTag(Func<TrAttrTemplateBuilder, TrAttrTemplateBuilder> trBuilderAction)
        {
            string htmlTempl = "<tr {0}></tr>";
            string trAttrInner = null;
            if (trBuilderAction != null)
            {
                TrAttrTemplateBuilder builder = new TrAttrTemplateBuilder();
                builder = trBuilderAction(builder);
                if(builder != null)
                {
                    TrAttrTemplateContext templContext = new TrAttrTemplateContext { TrAttrPrefix = "ng" };
                    builder.Build()(templContext);
                    trAttrInner = templContext.GetAllString();
                }
            }
            string fullHtml = string.Format(htmlTempl, trAttrInner);
            Console.WriteLine(fullHtml);
        }
    }
}

 

运行截图:和 上图一样。

 

03-中间件设计(已提取公共代码)

代码:

SpaceMiddlewareBuilder.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DesignSample
{
    /// <summary>
    /// 空格中间件生成器
    /// </summary>
    /// <typeparam name="TDelegate">委托的类型</typeparam>
    public class SpaceMiddlewareBuilder<TDelegate, TDelegateParam, TChild>
        where TDelegate: Delegate
        where TDelegateParam: SpaceMiddlewareContext
        where TChild : SpaceMiddlewareBuilder<TDelegate, TDelegateParam, TChild>,new()
    {
        private readonly List<Func<TDelegate, TDelegate>> _middlewares
            = new List<Func<TDelegate, TDelegate>>();

        private TDelegate _defaultAction;

        /// <summary>
        /// 构造一个空格中间件生成器
        /// </summary>
        /// <param name="defaultAction">默认执行的动作。无论是否有注册中间件,都会默认执行,除非在某一个中间件中拒绝调用 next(context)。不能为 NULL</param>
        public SpaceMiddlewareBuilder(TDelegate defaultAction)
        {
            this._defaultAction = defaultAction ?? throw new ArgumentNullException(nameof(defaultAction));
        }

        /// <summary>
        /// 使用中间件
        /// </summary>
        /// <param name="middleware"></param>
        /// <returns></returns>
        public TChild Use(Func<TDelegate, TDelegate> middleware)
        {
            _middlewares.Add(middleware);
            return (TChild)this;
        }

        /// <summary>
        /// 生成
        /// </summary>
        /// <returns></returns>
        public TDelegate Build()
        {
            _middlewares.Reverse();
            TDelegate next = this._defaultAction;
            foreach (var middleware in _middlewares)
            {
                next = middleware(next);
            }
            return next;
        }

        /// <summary>
        /// 执行委托,返回附加的所有文本
        /// </summary>
        /// <param name="builderAction"></param>
        /// <param name="delegateParam"></param>
        /// <returns></returns>
        public string Execute(Func<TChild, TChild> builderAction, TDelegateParam delegateParam)
        {
            if(builderAction == null)
            {
                return string.Empty;
            }
            TChild builder = new TChild();
            builder = builderAction(builder);
            if (builder == null)
            {
                return string.Empty;
            }
            TDelegate tDelegate = builder.Build();
            if(tDelegate == null)
            {
                return string.Empty;
            }
            tDelegate.DynamicInvoke(delegateParam);
            return delegateParam.GetAllText();
        }
    }
}

 

SpaceMiddlewareContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DesignSample
{
    /// <summary>
    /// 空格中间件上下文
    /// </summary>
    public class SpaceMiddlewareContext
    {
        private readonly StringBuilder _textBuilder;

        /// <summary>
        /// 构造函数
        /// </summary>
        public SpaceMiddlewareContext()
        {
            _textBuilder = new StringBuilder();
        }

        /// <summary>
        /// 增加一些文本字符串
        /// </summary>
        /// <param name="text"></param>
        public void Add(string text)
        {
            if (_textBuilder.Length > 0)
            {
                _textBuilder.Append(" ");
            }
            _textBuilder.Append(text);
        }

        /// <summary>
        /// 获取到目前为止所有增加的文本字符串集合
        /// </summary>
        /// <returns></returns>
        public string GetAllText()
        {
            return _textBuilder.ToString();
        }
    }
}

 

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignSample
{
    public class TrAttrTemplateContext : SpaceMiddlewareContext
    {
        public string TrAttrPrefix { get; set; }
    }

    public delegate void RequestDelegate(TrAttrTemplateContext arg);

    public class TrAttrTemplateBuilder : SpaceMiddlewareBuilder<RequestDelegate, TrAttrTemplateContext, TrAttrTemplateBuilder>
    {
        public TrAttrTemplateBuilder()
            : base(c => { c.Add($"{ c.TrAttrPrefix }-ending=\"true\""); })
        {

        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            //PrintTrTag(null); //Test 1
            //PrintTrTag(app => app); //Test 2
            PrintTrTag(app =>
                app.Use(AppendIdForTrTag) //给 tr 标签增加 ng-id 属性
                   .Use(AppendTimeForTrTag)  //给 tr 标签增加 ng-time 属性
            );//Test 3
        }

        /* 假设本方法来自于你们公司的B部门,通过封装,用于给<tr>标签固定附加 ng-id 属性 */
        static RequestDelegate AppendIdForTrTag(RequestDelegate next) => context =>
        {
            context.Add($"{ context.TrAttrPrefix }-id=\"tr1\"");
            next(context);
        };

        /* 假设本方法来自于你们公司的A部门,通过封装,用于给<tr>标签固定附加 ng-time 属性 */
        static RequestDelegate AppendTimeForTrTag(RequestDelegate next) => context =>
        {
            context.Add($"{ context.TrAttrPrefix }-time=\"{ DateTime.Now.ToString() }\"");
            next(context);
        };

        /* 假设本方法来自于ASP.NET Core内部。用于给<tr>标签附加一系列以 ng- 开头的属性 */
        static void PrintTrTag(Func<TrAttrTemplateBuilder, TrAttrTemplateBuilder> trBuilderAction)
        {
            string htmlTempl = "<tr {0}></tr>";
            string trAttrInner = new TrAttrTemplateBuilder().Execute(trBuilderAction, new TrAttrTemplateContext
            {
                TrAttrPrefix = "ng"
            });
            string fullHtml = string.Format(htmlTempl, trAttrInner);
            Console.WriteLine(fullHtml);
        }
    }
}

 

谢谢浏览!