设计模式-职责链模式(ChainOfResponsibility)

讲故事

书接上文,状态模式完美解决了多判断分支分支问题,符合了我人生信条的第一、第三条。今天来探讨一下状态模式异父异母的亲兄弟职责链模式,它们都有异曲同工之妙,实际开发中可根据口味,自行选用。

今天的故事背景就放在我们平时 申请加薪、请假等活动中,我们都知道,随着我们申请内容的不同,审批人员的等级也不同。我们就先用最简单的代码模拟一下

Coding

RequestType枚举,规范我们申请的类型

    /// <summary>
    /// 请求类型
    /// </summary>
    public enum RequestType
    {
        /// <summary>
        /// 请假
        /// </summary>
        Leave,

        /// <summary>
        /// 加薪
        /// </summary>
        PayRise
    }

Requset类,简单描述申请内容

    /// <summary>
    /// 请求
    /// </summary>
    public class Requset
    {
        /// <summary>
        /// 请求类型
        /// </summary>
        public RequestType Type { get; set; }

        /// <summary>
        /// 请求数量
        /// </summary>
        public int Number { get; set; }

        /// <summary>
        /// 请求说明
        /// </summary>
        public string Content
        {
            get
            {
                switch (Type)
                {
                    case RequestType.Leave:
                        return $"请假{Number}天";

                    case RequestType.PayRise:
                        return $"加薪{Number}元";

                    default:
                        return "未知";
                }
            }
        }
    }

ManagerType枚举,定义不同的审批人员类型

    /// <summary>
    /// 管理者类型
    /// </summary>
    public enum ManagerType
    {
        PM,
        CTO,
        CEO
    }

Manager类,审批人员,处理我们的请求

    public class Manager
    {
        private readonly ManagerType _managerType;

        public Manager(ManagerType managerType)
        {
            _managerType = managerType;
        }

        /// <summary>
        /// 处理请求
        /// </summary>
        public void Process(Requset requset)
        {
            if (_managerType == ManagerType.PM)
            {
                if (requset.Type == RequestType.Leave && requset.Number <= 2)
                {
                    Console.WriteLine($"项目经理 已批准 你的 {requset.Content} 申请");
                }
                else
                {
                    Console.WriteLine($"项目经理 无权批准 你的 {requset.Content} 申请");
                }
            }
            else if (_managerType == ManagerType.CTO)
            {
                if (requset.Type == RequestType.Leave)
                {
                    if (requset.Number <= 5)
                    {
                        Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申请");
                    }
                    else
                    {
                        Console.WriteLine($"CTO 无权批准 你的 {requset.Content} 申请");
                    }
                }
                else
                {
                    if (requset.Number <= 500)
                    {
                        Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申请");
                    }
                    else
                    {
                        Console.WriteLine($"CTO 无权批准 你的 {requset.Content} 申请");
                    }
                }
            }
            else if (_managerType == ManagerType.CEO)
            {
                if (requset.Type == RequestType.Leave)
                {
                    Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申请");
                }
                else
                {
                    if (requset.Number <= 1000)
                    {
                        Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申请");
                    }
                    else
                    {
                        Console.WriteLine($"CEO对你的 {requset.Content} 申请 说:“小子,你有点飘啊!”");
                    }
                }
            }
        }
    }

客户端

    internal class Program
    {
        private static void Main(string[] args)
        {
            //创建领导
            var pm = new Manager(ManagerType.PM);
            var cto = new Manager(ManagerType.CTO);
            var ceo = new Manager(ManagerType.CEO);

            //创建 请假请求
            var request1 = new Requset
            {
                Type = RequestType.Leave,
                Number = 5
            };

            pm.Process(request1);
            cto.Process(request1);
            ceo.Process(request1);
            Console.WriteLine("\n");

            //创建 加薪请求
            var request2 = new Requset
            {
                Type = RequestType.PayRise,
                Number = 2000
            };
            pm.Process(request2);
            cto.Process(request2);
            ceo.Process(request2);

            Console.WriteLine("\nHappy Ending~");
            Console.ReadLine();
        }
    }

结果展示:

项目经理 无权批准 你的 请假5天 申请
CTO 已批准 你的 请假5天 申请
CEO 已批准 你的 请假5天 申请


项目经理 无权批准 你的 加薪2000元 申请
CTO 无权批准 你的 加薪2000元 申请
CEO对你的 加薪2000元 申请 说:“小子,你有点飘啊!”

我们Code Review后,就会发现Manager.Process()又丑又长呀,同样是我们选侍女时出现的三个问题:方法长,分支多,难维护。

那我们怎么使用职责链模式来解决呢?我们先来了解一下它...

职责链模式

敲黑板·划重点

定义: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一个链,并沿着这条链传递请求,知道有一个对象处理请求为止。

好处: 接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需要保持它所有的候选接受者的引用。【降低耦合度】

注意: 一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理,需要考虑全面!

在下面代码优化中来深化对定义、好处的理解吧

Code Upgrade

Manager抽象类,不同权限审批人员的基类,可以设置下一级审批人员,形成一条职责链

    public abstract class Manager
    {
        protected readonly ManagerType _managerType;
        
        /// <summary>
        /// 下一个处理者
        /// </summary>
        protected Manager Successor { get; private set; }

        public Manager(ManagerType managerType)
        {
            _managerType = managerType;
        }

        /// <summary>
        /// 设置下一个处理者
        /// </summary>
        /// <param name="manager"></param>
        public void SetSuccessor(Manager manager)
        {
            Successor = manager;
        }

        /// <summary>
        /// 处理请求
        /// </summary>
        /// <param name="requset"></param>
        public abstract void Process(Requset requset);
    }

PMCTOCEO类,具体的审批人,实现处理方法Process()

    public class PM : Manager
    {
        public PM(ManagerType managerType) : base(managerType)
        {
        }

        public override void Process(Requset requset)
        {
            if (requset.Type == RequestType.Leave && requset.Number <= 2)
            {
                Console.WriteLine($"项目经理 已批准 你的 {requset.Content} 申请");
                return;
            }

            if (Successor != null)
            {
                //交由下一级处理
                Successor.Process(requset);
            }
        }
    }

    public class CTO : Manager
    {
        public CTO(ManagerType managerType) : base(managerType)
        {
        }

        public override void Process(Requset requset)
        {
            if (requset.Type == RequestType.Leave && requset.Number <= 5)
            {
                Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申请");
                return;
            }

            if (requset.Type == RequestType.PayRise && requset.Number <= 500)
            {
                Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申请");
                return;
            }
            if (Successor != null)
            {
                //交由下一级处理
                Successor.Process(requset);
            }
        }
    }

    public class CEO : Manager
    {
        public CEO(ManagerType managerType) : base(managerType)
        {
        }

        public override void Process(Requset requset)
        {
            if (requset.Type == RequestType.Leave && requset.Number <= 15)
            {
                Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申请");
                return;
            }

            if (requset.Type == RequestType.PayRise && requset.Number <= 1000)
            {
                Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申请");
                return;
            }
            Console.WriteLine($"CEO对你的 {requset.Content} 申请 说:“小子,你有点飘啊!”");
        }
    }

客户端代码

    internal class Program
    {
        private static void Main(string[] args)
        {
            //创建领导
            var pm = new PM(ManagerType.PM);
            var cto = new CTO(ManagerType.CTO);
            var ceo = new CEO(ManagerType.CEO);
            //设置下一级处理者
            pm.SetSuccessor(cto);
            cto.SetSuccessor(ceo);

            //创建 请假请求
            var request1 = new Requset
            {
                Type = RequestType.Leave,
                Number = 5
            };
            pm.Process(request1);
            Console.WriteLine("\n");

            //创建 加薪请求
            var request2 = new Requset
            {
                Type = RequestType.PayRise,
                Number = 2000
            };
            pm.Process(request2);

            Console.WriteLine("\nHappy Ending~");
            Console.ReadLine();
        }
    }

结果展示:

CTO 已批准 你的 请假5天 申请

CEO对你的 加薪2000元 申请 说:“小子,你有点飘啊!”

这样我们也消除上面三个问题。当需求需要人事加入审批流程时,只需要增加一个继承Manager的人事类;当需求要求不需要CTO审批,直接由PM转到CEO时,我们只用修改PM的SetSuccessor(),这也体现了职责链的灵活性。

示例代码地址: https://gitee.com/sayook/DesignMode/tree/master/ChainOfResponsibility

posted @ 2020-04-27 17:23  稻草堆上打着滚儿  阅读(393)  评论(0编辑  收藏  举报