Middleware的艺术

定义

Middleware直译叫中间件,目前在百度上很难找到一个简单明了的含义解释,.Net下以前也比较难以看到它的身影,但在Microsoft.Owin里,多个地方都看到MiddleWare,我近来在尝试理解Middleware,并在实际中模仿应用,本文章将我的个人理解和大家分享一下。

Middleware的抽象

Microsoft.Owin的Middleware

public abstract class OwinMiddleware
{
    protected OwinMiddleware(OwinMiddleware next);

    protected OwinMiddleware Next { get; set; }

    public abstract Task Invoke(IOwinContext context);
}

微软把Middleware定义在抽象类,除了Invoke的中间件执行入口,还有一个Next的中间件属性,两者都是非常抽象。Invoke正是本中间件的处理逻辑,参数IOwinContext包含问题试卷和答题卡,Next表示下一个中间件,如果本中间件不想解决此问题,那么就让Next来解决这个问题,以此类推。提问题的人不用关注是谁回答了问题,他只关注答案了够了。

简单的Middleware接口

如果我们把中间件定义为接口,那么一个简单的中间件接口可以如下:

public interface IMiddleware
{
    IMiddleware Next { set; }
    Answer GetAnswer(Question question);
}

当然,在一些实际项目中,中间件的抽象程度不一样,所以中间件的执行方法是根据实际需要作合理的设计,但Next一般不会有变化,它都是代表下一个中间件。

Middleware和插件的区别

插件是应用程序的功能扩展,它可以只关注自身的功能的实现,它可能不是带问题的,但各插件的执行接口是一样的,最简单的插件接口可以如下:

public interface IPlug
{
    // 加载执行插件
    void Init();
}

Middleware是问题解决单元抽象,它往往是由多个单元同时协同工作,各单元虽然没有直接关系,但Next属性还是得到了下个中间件的实例,提供给本中间件来传递。Middlware随着数量的增多,解决的问题各类也可以越多,和插件越多功能越丰富有相似。

Middleware的使用场景

我曾经写过《.Net下一个类型转换神器》,近期在代码重构,发现这玩意很适合使用中间件来实现。在类型转换中,调用者想要的是这种:

/// <summary>
/// 将value转换为目标类型
/// </summary>
/// <param name="value">要转换的值</param>
/// <param name="targetType">转换的目标类型</param>
/// <returns></returns>
object Convert(object value, Type targetType);

value和targetType千变万化,一口可吃不完,本着单一职责原则,我们应该要写很多转换单元,每个单元只负责一种类型转换,转换器由N多个转换单元组成,如果转换单元是中间件,抽象的中间件接口应该如下:

    /// <summary>
    /// 定义类型转换单元的接口
    /// </summary>
    public interface IConvertMiddleware
    {
        /// <summary>
        /// 设置下一个转换单元
        /// </summary>
        IConvertMiddleware Next { set; }

        /// <summary>
        /// 将value转换为目标类型
        /// </summary>
        /// <param name="上下文">context</param>
        /// <returns></returns>
        object Convert(ConvertContext context);
    }

实际当中,我们可能未必需要这么抽象,我的实际接口是

    /// <summary>
    /// 定义类型转换单元的接口
    /// </summary>
    public interface IConvert
    {
        /// <summary>
        /// 设置转换器
        /// </summary>
        Converter Converter { set; }

        /// <summary>
        /// 设置下一个转换单元
        /// </summary>
        IConvert NextConvert { set; }

        /// <summary>
        /// 将value转换为目标类型
        /// </summary>
        /// <param name="value">要转换的值</param>
        /// <param name="targetType">转换的目标类型</param>
        /// <returns></returns>
        object Convert(object value, Type targetType);
    }

然后,我们来一个一个地来实现转换单元:

某个单元的实现如下:

    /// <summary>
    /// 表示不作转换的转换单元
    /// </summary>
    public class NoConvert : IConvert
    {
        /// <summary>
        /// 转换器实例
        /// </summary>
        public Converter Converter { get; set; }

        /// <summary>
        /// 下一个转换单元
        /// </summary>
        public IConvert NextConvert { get; set; }

        /// <summary>
        /// 将value转换为目标类型
        /// </summary>
        /// <param name="value">要转换的值</param>
        /// <param name="targetType">转换的目标类型</param>
        /// <returns></returns>
        public object Convert(object value, Type targetType)
        {
            if (targetType == typeof(object))
            {
                return value;
            }

            if (value != null && targetType == value.GetType())
            {
                return value;
            }

            return this.NextConvert.Convert(value, targetType);
        }
    }
NoConvert

更广泛的Middleware

我近来在开发开源项目《NetworkSocket》,自从看到Microsoft.Owin之后的中间件之后,觉得Middleware可以在很多领域中使用,现在这个项目里的协议解析实现者都已经完全由中间件来开发,功能越来丰富,但复杂度感觉没有增大。看看下面代码,你应该能感觉到Middleware的甜味。

var listener = new TcpListener();
listener.Use<HttpMiddleware>();
listener.Use<JsonWebSocketMiddleware>();
listener.Use<FastMiddleware>();            
listener.Start(1212);

 

posted @ 2016-01-04 23:36  jiulang  阅读(1688)  评论(6编辑  收藏  举报