C#设计模式-责任链模式(Chain of Responsibility Pattern)
引子
一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,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…语句基本上就没法看了。
- 耦合度高:如果我们想继续添加处理请求的类,那么就要继续添加else if判定条件;另外,这个条件判定的顺序也是写死的,如果想改变顺序,那么也只能修改这个条件语句。
在设计模式中提倡单一职责原则,如果项目组内再加一个组长,审批请假小于一天的呢?此时就会感觉 if-else 灵活性太差,修改代码后测试需要重新测试全部流程才能保证质量。
既然已经清楚他的不足,则针对此业务逻辑可以稍作转换:如果满足条件1,则由 Handler1 来处理,不满足则向下传递;如果满足条件2,则由 Handler2 来处理,不满足则继续向下传递,以此类推,直到条件结束。其实改进的方法也很简单,就是把判定条件的部分放到处理类中,这就是责任连模式的原理。
定义
责任链模式属于行为类模式。使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
责任链模式把多个处理器串成链,然后让请求在链上传递:

类图
从的定义可以看出涉及的对象只有处理者角色,但可以有多个处理者,这些处理者做的事情都是一样的,处理请求的方法,所以可以抽象出一个处理者角色进行代码复用。如下类图。

角色
- Handler(抽象处理类):抽象处理类中主要包含一个指向下一处理类的成员变量nextHandler和一个处理请求的方法handRequest,handRequest方法的主要主要思想是,如果满足处理的条件,则有本处理类来进行处理,否则由nextHandler来处理。
- ConcreteHandler(具体处理类):具体处理类主要是对具体的处理逻辑和处理的适用条件进行实现。
实现
将上面的请假流程重新梳理,使用责任链模式进行实现:
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语句的情况下,此时可以考虑使用责任链模式来对代码进行重构。
- 可动态指定一组对象处理请求,客户端可以动态创建职责链来处理请求,还可以改变链中处理者之间的先后次序。
优缺点
通过上面的介绍很容易发现,责任链模式的优点:
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 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
C#设计模式-责任链模式(Chain of Responsibility Pattern)的更多相关文章
- 23种设计模式--责任链模式-Chain of Responsibility Pattern
一.责任链模式的介绍 责任链模式用简单点的话来说,将责任一步一步传下去,这就是责任,想到这个我们可以相当击鼓传花,这个是为了方便记忆,另外就是我们在项目中经常用到的审批流程等这一类的场景时我们就可以考 ...
- 乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern)
原文:乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 责任链模式(Chain of R ...
- 二十四种设计模式:责任链模式(Chain of Responsibility Pattern)
责任链模式(Chain of Responsibility Pattern) 介绍为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求.将这些对象连成一条链,并沿着这条链传递该请求,直 ...
- 设计模式-责任链模式Chain of Responsibility)
一.定义 职责链模式是一种对象的行为模式.在职责链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...
- C#设计模式——职责链模式(Chain Of Responsibility Pattern)
一.概述 在软件开发中,某一个对象的请求可能会被多个对象处理,但每次最多只有一个对象处理该请求,对这类问题如果显示指定请求的处理对象,那么势必会造成请求与处理的紧耦合,为了将请求与处理解耦,我们可以使 ...
- 责任链模式 职责链模式 Chain of Responsibility Pattern 行为型 设计模式(十七)
责任链模式(Chain of Responsibility Pattern) 职责链模式 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系 将这些对象连接成一条链,并沿着这 ...
- 责任链模式-Chain of Responsibility(Java实现), 例2
责任链模式-Chain of Responsibility 在这种模式中,通常每个接收者都包含对另一个接收者的引用.如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推. 咱们在 ...
- 责任链模式-Chain of Responsibility(Java实现), 例1
责任链模式-Chain of Responsibility, 例1 在这种模式中,通常每个接收者都包含对另一个接收者的引用.如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推. ...
- 《JAVA设计模式》之责任链模式(Chain of Responsibility)
在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...
随机推荐
- 【题解】[USACO07NOV]Sunscreen G
\(Link\) \(\text{Solution:}\) 把奶牛的忍耐度转化为线段,则题目转化为选择一些点使得覆盖的线段尽可能多.一个点只能覆盖一条线段. 考虑将点按照位置排序,线段按照右端点排序. ...
- H264裸码流I/P/B帧类型判别
花了两天时间做了个h264裸流nal类型和frame类型检测的工具,已上传至github,有需要的自行下载. 1.NAL类型检测 nal类型检测非常容易,对照下表即可容易判断类型. 较常用nal类型包 ...
- 联赛模拟测试12 B. trade
题目描述 分析 \(n^2\) 的 \(dp\) 应该比较好想 设 \(f[i][j]\) 为当前在第 \(i\) 天剩余的货物数量为 \(j\) 时的最大收益 那么它可以由 \(f[i-1][j]\ ...
- JVM性能调优(3) —— 内存分配和垃圾回收调优
前序文章: JVM性能调优(1) -- JVM内存模型和类加载运行机制 JVM性能调优(2) -- 垃圾回收器和回收策略 一.内存调优的目标 新生代的垃圾回收是比较简单的,Eden区满了无法分配新对象 ...
- 关于ptype_all和pypte_base中的pt_prev的说明[转]
不知道原帖,我是从这里看到了,解决了迷惑我很久的疑问,抄过来. 看见noble_shi兄弟"关于net_rx_action函数的若干问题"贴中关于pt_prev的问题, 本来想在论 ...
- centos8平台使用iostat监控磁盘io
一,iostat所属的包: [root@centos8 ~]# whereis iostat iostat: /usr/bin/iostat /usr/share/man/man1/iostat.1. ...
- lumen单元测试
phpunit --filter testInfo tests/UserTest.php UserTest.php <?php use Laravel\Lumen\Testing\Databa ...
- javascript 数字 字母 互转
var alphabet= String.fromCharCode(64 + parseInt(填写数字); 单个字符转数字: 'a'.charCodeAt(0) 结果: 97 数字转字母: Stri ...
- Topsis优劣解距离法 mlx代码
请参考https://blog.csdn.net/qq_36384657/article/details/98188769 mlx代码 topsis 优劣解距离法 参数说明: 分数.获奖次数.价值等 ...
- Deepin v15.11驱动安装问题
最近想用Linux跑深度学习,试了好几个发行版,最终选择了Deepin v15.11,但由于配置比较新,它不能兼容很多驱动,还得自己装,以下是我失败N次后得到的经验: 电脑配置 配置如下: 型号:DE ...