[设计模式] 责任链模式之变种 – 责任链子

今天看到 wikipedia 的责任链模式,好奇之下搜寻相关文章,此文章主要参考

91大的 [.NET]重构之路系列v11 –用责任链模式打破讨厌的switch case

的变种作法,所以跟原本的责任链模式的设计思路不太相同,但结果相同,

所以阅读前,建议先看完91大的文章后,在来看这篇会比较能感觉出差异。


 这边先引用91大文章结尾结论中的补充来复习一下责任链的重点,

20120415 补充:责任链的用意在于把每个角色的职责分清楚,每个对象只需要做好自己的事,透过责任链的方式来组合,并完成使用场景的需求。各角色的职责如下:

  1. 链上的每个责任对象,只知道‘针对什么情况,代表是自己的职责’,以及只知道‘自己的职责,应该要做什么事’。
  2. 抽象的责任对象,只知道‘往下传递’这件事。
  3. 使用场景,只知道‘调用这条责任链处理’,就可以得到想要的结果。


责任

第2点也是我想改变的部分,如果系统中真的有很多地方方需要用到责任链模式时,

我会看到一堆的抽象责任对象做负责往下传递的这件事情,

所以我希望每个责任对象只做好自己要做什么事情,还有是不是自己的责任就好,

所以每个责任对象没有继承抽象责任对象的往下传递这个的相关代码:

        public interface IMonkey
        {
            bool DoSomething(DayOfWeek today);
        }

        public abstract class AbstractMonkey : IMonkey
        {
            public virtual bool DoSomething(DayOfWeek today)
            {
                // 是自己的责任,就做
                var result = IsMyResponsibility(today);
                if (result)
                    MyAction(today);
                return result;
            }

            protected abstract void MyAction(DayOfWeek today);
            protected abstract bool IsMyResponsibility(DayOfWeek today);
        }

        public class MonkeyMonday : AbstractMonkey
        {
            protected override void MyAction(DayOfWeek today)
            {
                Console.WriteLine("星期一,猴子穿新衣");
            }

            protected override bool IsMyResponsibility(DayOfWeek today)
            {
                return today == DayOfWeek.Monday;
            }
        }

        public class MonkeyTuesday : AbstractMonkey
        {
            protected override void MyAction(DayOfWeek today)
            {
                Console.WriteLine("星期二,猴子肚子饿");
            }

            protected override bool IsMyResponsibility(DayOfWeek today)
            {
                return today == DayOfWeek.Tuesday;
            }
        }

        public class MonkeySunday : AbstractMonkey
        {
            protected override void MyAction(DayOfWeek today)
            {
                Console.WriteLine("星期日,猴子过生日");
            }

            protected override bool IsMyResponsibility(DayOfWeek today)
            {
                return today == DayOfWeek.Sunday;
            }
        }

从上面看到 AbstractMonkey 只单纯使用模板方法,要求实践的猴子都要实践 MyAction 与 IsMyResponsibility,

而 AbstractMonkey 实践 IMonkey.DoSomething 做某些事情的流程,并回传是否有做了。


链子

上面那些代码,只做了责任对象应该做什么事情,也就是责任有了,那接着我就要做一个链子:

        public class Chain
        {
            private IList _chain = new List();

            public Chain SetNext(T item)
            {
                _chain.Add(item);
                return this;
            }

            public void Execute(Func doSomething)
            {
                foreach (var item in _chain)
                {
                    if (doSomething(item))
                        break;
                }
            }
        }

 链子的用意就是‘往下传递’,所以有 SetNext 可设定下一个责任对象,然后使用 Execute 的委派参数来检查某一个对象的回传是否停止做。


使用方式

        static void Main(string[] args)
        {
            var today = DateTime.Today.DayOfWeek;
            var monkeyChain = new Chain()
                .SetNext(new MonkeyMonday())
                .SetNext(new MonkeyTuesday())
                .SetNext(new MonkeySunday());

            monkeyChain.Execute(monkey => monkey.DoSomething(today));
        }

先用 SetNext 将 IMonkey 的实践串在一起,在透过 IMonkey.DoSomething 执行责任链。

执行结果


小结

这个是责任链的变种,主要拆成责任跟链子,

为了要在使用责任链时,让责任链写起来更无负担,

以下是重复使用链子后,能减少的代码(引用91大的部分代码作为范例):

1. 减少写使用构造函数(或方法)设定下一个责任对象的代码。

        public AbstractMonkey(AbstractMonkey monkey)
        {
            this._nextMonkey = monkey;
        }

2. 减少写保存下一个责任对象字段私有字段代码。

private AbstractMonkey _nextMonkey;

3. 减少写如何执行下一个责任对象的代码。

        public void DoSomething(DayOfWeek today)
        {
            if (isMyResponsibility(today))
            {
                this.MyAction(today);
            }
            else
            {
                if (this._nextMonkey != null)
                {
                    this._nextMonkey.DoSomething(today);
                }
                else
                {
                    Console.WriteLine("责任链接束!");
                }

            }
        }


参考文章

[.NET]重构之路系列v11 –用责任链模式打破讨厌的switch case

设计模式(17)-Chain of Responsibility Pattern