一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现。
现在以请假流程为例,一般公司普通员工的请假流程简化如下:
普通员工发起一个请假申请,当请假天数小于3天时只需要得到主管批准即可;当请假天数大于3天时,主管批准后还需要提交给经理审批,经理审批通过,若请假天数大于7天还需要进一步提交给总经理审批。
简单的流程可以通过 if-else 即可实现:
public class Leave { public void leaveApproval(int leaveDays) { if (leaveDays < 3) { Console.WriteLine("项目经理审批"); } else if (leaveDays < 7) { Console.WriteLine("部门经理审批"); } else if (leaveDays < 30) { Console.WriteLine("总经理审批"); } else { Console.WriteLine("审批困难"); } } }
但是这样的写法看起来简单,后续维护难度却是不少。可以看出代码臃肿且耦合度高。
在设计模式中提倡单一职责原则,如果项目组内再加一个组长,审批请假小于一天的呢?此时就会感觉 if-else 灵活性太差,修改代码后测试需要重新测试全部流程才能保证质量。
既然已经清楚他的不足,则针对此业务逻辑可以稍作转换:如果满足条件1,则由 Handler1 来处理,不满足则向下传递;如果满足条件2,则由 Handler2 来处理,不满足则继续向下传递,以此类推,直到条件结束。其实改进的方法也很简单,就是把判定条件的部分放到处理类中,这就是责任连模式的原理。
责任链模式属于行为类模式。使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
责任链模式把多个处理器串成链,然后让请求在链上传递:
从的定义可以看出涉及的对象只有处理者角色,但可以有多个处理者,这些处理者做的事情都是一样的,处理请求的方法,所以可以抽象出一个处理者角色进行代码复用。如下类图。
将上面的请假流程重新梳理,使用责任链模式进行实现:
using System; namespace 责任链模式 { class Program { static void Main(string[] args) { LeaveRequest leaveTwoDays = new LeaveRequest(2, "grey1"); LeaveRequest leaveSixDays = new LeaveRequest(6, "grey2"); LeaveRequest leaveEightDays = new LeaveRequest(8, "grey3"); Approver PM = new Manager("jon1"); Approver DM = new DepartmentManager("jon2"); Approver GM = new GeneralManager("jon3"); // 设置责任链 PM.NextApprover = DM; DM.NextApprover = GM; // 处理请求 PM.LeaveRequest(leaveTwoDays); PM.LeaveRequest(leaveSixDays); PM.LeaveRequest(leaveEightDays); Console.ReadLine(); } } // 请假需求 public class LeaveRequest { public int Day { get; set; } public string Name { get; set; } public LeaveRequest(int day, string name) { this.Day = day; this.Name = name; } } // 审批人 public abstract class Approver { public Approver NextApprover { get; set; } public string Name { get; set; } public Approver(string name) { this.Name = name; } public abstract void LeaveRequest(LeaveRequest requeset); } // 项目经理 public class Manager : Approver { public Manager(string name) : base(name) { } public override void LeaveRequest(LeaveRequest requeset) { if (requeset.Day < 3) { Console.WriteLine("项目经理 {0} 审批 {1} 请假", this.Name, requeset.Name); } else { NextApprover.LeaveRequest(requeset); } } } // 部门经理 public class DepartmentManager : Approver { public DepartmentManager(string name) : base(name) { } public override void LeaveRequest(LeaveRequest requeset) { if (requeset.Day < 7) { Console.WriteLine("部门经理 {0} 审批 {1} 请假", this.Name, requeset.Name); } else { NextApprover.LeaveRequest(requeset); } } } // 总经理 public class GeneralManager : Approver { public GeneralManager(string name) : base(name) { } public override void LeaveRequest(LeaveRequest requeset) { if (requeset.Day < 30) { Console.WriteLine("总经理 {0} 审批 {1} 请假", this.Name, requeset.Name); } else { Console.WriteLine("审批困难"); ; } } } }
运行一下:
项目经理 jon1 审批 grey1 请假
部门经理 jon2 审批 grey2 请假
总经理 jon3 审批 grey3 请假
LeaveRequest 类为请求请假。
Approver 为处理人员。并且设置了三个处理人员,Manager、DepartmentManager、GeneralManager。
设置的责任链为 Manager-->DepartmentManager-->GeneralManager。当发生请假请求时首先由Manager进行处理,处理不了转由DepartmentManager,如果DepartmentManager还是处理不了则继续向更好职位的人员GeneralManager进行提交,由更大权限的人进行处理。
实现的功能和文章最初的 if...else 一样。但时可以看到使用责任链模式代码更清楚,请求发送者是发送者,接收者是接收者。
通过上面的定义、类图及示例可以考虑责任链模式适用的场景:
通过上面的介绍很容易发现,责任链模式的优点:
但也有缺点:
纯的责任链模式:
不纯的责任链模式:
责任链模式其实就是一个灵活版的if…else…语句,将这些判定条件的语句放到了各个处理类中,非常灵活。
责任链模式是一种把多个处理器组合在一起,依次处理请求的模式。
责任链降低了请求端和接收端之间的耦合,使多个对象都有机会处理某个请求。
责任链模式经常用在拦截、预处理请求等。
与此同样也带来了风险,比如设置处理类前后关系时,一定要特别仔细,搞对处理类前后逻辑的条件判断关系,并且注意不要在链中出现循环引用的问题。
https://juejin.im/post/6844903702260629512
https://www.w3cschool.cn/javadesignpattern/omas1ii2.html
https://www.cnblogs.com/zhili/p/ChainOfResponsibity.html
http://c.biancheng.net/view/1383.html
https://www.liaoxuefeng.com/wiki/1252599548343744/1281319474561057