责任链模式如同aspnetcore中的管道机制,贯穿真个框架的开始和结束。很经典的有请假,写个假条找组长,主管 ,经理,boss等一层一层的去批复,当然每个领导批假的天数是具体明确的,要不然就没法传递下去,这里有两个字很关键,就是“传递”。

下面通过一个简单的计算器的例子具体说明。

 public class CalContext
    {
        public decimal[] Args { get; set; }
        public Operator calOperator { get; set; }
        public decimal Result { get; set; }
    }
    public enum Operator
    {
        Add,
        Sub,
        Mul,
        Div
    }

责任链的上下文文本很重要,跟aspnetcore里面的httpcontext一样,起到传递数据承载作用。

  public abstract class CalChainBase
    {
        private CalChainBase _chainBase; //可以通过可空构造函数,效果一样

        public CalChainBase ChainBase { get => _chainBase; set => _chainBase = value; }

        public abstract CalContext Invoke(CalContext context);
    }

上面的代码就是整个链子的传递核心所在,依然是抽象功不可没,CalChainBase看起来就是一个对象,但是它确实一类的抽象,正因为这样所以我们可以通过相同的特征对象来不断的替换它,每次替换都可以完成自己的invoke任务,而这个上下文文本CalContext就是整个数据传输的载体。

   public class AddChain : CalChainBase
    {
        public override CalContext Invoke(CalContext context)
        {
            if (context.calOperator.Equals(Operator.Add))
            {
                context.Result = context.Args.Aggregate((x, y) => (x + y));
                return context;
            }

            if (ChainBase == null)
                throw new ArgumentNullException(nameof(AddChain));
            return ChainBase.Invoke(context);
        }
    }
    public class SubChain : CalChainBase
    {
        public override CalContext Invoke(CalContext context)
        {
            if (context.calOperator.Equals(Operator.Sub))
            {
                context.Result = context.Args.Aggregate((x, y) => (x - y));
                return context;
            }

            if (ChainBase == null)
                throw new ArgumentNullException(nameof(SubChain));
            return ChainBase.Invoke(context);
        }
    }
    public class MulChain : CalChainBase
    {
        public override CalContext Invoke(CalContext context)
        {
            if (context.calOperator.Equals(Operator.Mul))
            {
                context.Result = context.Args.Aggregate((x, y) => (x * y));
                return context;
            }

            if (ChainBase == null)
                throw new ArgumentNullException(nameof(MulChain));
            return ChainBase.Invoke(context);
        }
    }
    public class DivChain : CalChainBase
    {
        public override CalContext Invoke(CalContext context)
        {
            if (context.calOperator.Equals(Operator.Div))
            {
                context.Result = context.Args.Aggregate((x, y) => (x / y));
                return context;
            }

            if (ChainBase == null)
                throw new ArgumentNullException(nameof(DivChain));
            return ChainBase.Invoke(context);
        }
    }

上面的实现顺理成章,通过枚举判断具体的实例,面向对象理解好实例(对象)和类之间的关系尤其重要。这个判断就是把本分之内的事情给做了,如果不归我的话那就往下传递。这里可以给一个终结点输出点东西,提示一下context执行完没有被实际处理,这里我省略。

internal class Program
    {
        static void Main(string[] args)
        {
            CalContext context = new CalContext() { Args = new decimal[] { 1, 2, 3, 4, 5 }, calOperator = Operator.Add };
            CalChainBase start = new AddChain();
            CalChainBase sub = new SubChain();
            CalChainBase mul = new MulChain();
            CalChainBase div = new DivChain();
            start.ChainBase = sub;
            sub.ChainBase = mul;
            mul.ChainBase = div;

            start.Invoke(context);
            Console.WriteLine($"+ result= {context.Result}");

            context.calOperator = Operator.Sub;
            start.Invoke(context);
            Console.WriteLine($"- result= {context.Result}");

            context.calOperator = Operator.Mul;
            start.Invoke(context);
            Console.WriteLine($"* result= {context.Result}");

            context.calOperator = Operator.Div;
            start.Invoke(context);
            Console.WriteLine($"/ result= {context.Result}");

            context.Args = new decimal[] { 100, 200 };
            start.Invoke(context);
            Console.WriteLine($"/ result= {context.Result}");
        }
    }

 

上面的客户端实现就顺理成章了,可以在包装一下,这里省略。

总结,责任链也是处理对象与对象之间的关系,只不过它巧妙地把归于抽象类或者接口的一组实现抽象出来CalChainBase这样的模板。