设计模式-职责链模式(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); }
PM、CTO、CEO类,具体的审批人,实现处理方法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