引子

一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,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)的更多相关文章

  1. 23种设计模式--责任链模式-Chain of Responsibility Pattern

    一.责任链模式的介绍 责任链模式用简单点的话来说,将责任一步一步传下去,这就是责任,想到这个我们可以相当击鼓传花,这个是为了方便记忆,另外就是我们在项目中经常用到的审批流程等这一类的场景时我们就可以考 ...

  2. 乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern)

    原文:乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 责任链模式(Chain of R ...

  3. 二十四种设计模式:责任链模式(Chain of Responsibility Pattern)

    责任链模式(Chain of Responsibility Pattern) 介绍为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求.将这些对象连成一条链,并沿着这条链传递该请求,直 ...

  4. 设计模式-责任链模式Chain of Responsibility)

    一.定义 职责链模式是一种对象的行为模式.在职责链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...

  5. C#设计模式——职责链模式(Chain Of Responsibility Pattern)

    一.概述 在软件开发中,某一个对象的请求可能会被多个对象处理,但每次最多只有一个对象处理该请求,对这类问题如果显示指定请求的处理对象,那么势必会造成请求与处理的紧耦合,为了将请求与处理解耦,我们可以使 ...

  6. 责任链模式 职责链模式 Chain of Responsibility Pattern 行为型 设计模式(十七)

    责任链模式(Chain of Responsibility Pattern) 职责链模式 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系 将这些对象连接成一条链,并沿着这 ...

  7. 责任链模式-Chain of Responsibility(Java实现), 例2

    责任链模式-Chain of Responsibility 在这种模式中,通常每个接收者都包含对另一个接收者的引用.如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推. 咱们在 ...

  8. 责任链模式-Chain of Responsibility(Java实现), 例1

    责任链模式-Chain of Responsibility, 例1 在这种模式中,通常每个接收者都包含对另一个接收者的引用.如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推. ...

  9. 《JAVA设计模式》之责任链模式(Chain of Responsibility)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...

随机推荐

  1. springboot利用redis做缓存

    首先 配置redis redis: password: 123456 host: 127.0.0.1 port: 6379 #103.249.252.109:10086 expireSeconds: ...

  2. 开始接触flex

    flex框架使用的是.mxml后缀的文件,可以在Eclipse导入flex开发的插件.代码写完之后需要进行编译成为.swf文件成功之后才可以正常运行.现在刚开始接触金融的项目,需求什么的还有很多不是理 ...

  3. 推荐算法之: DeepFM及使用DeepCTR测试

    算法介绍 左边deep network,右边FM,所以叫deepFM 包含两个部分: Part1: FM(Factorization machines),因子分解机部分 在传统的一阶线性回归之上,加了 ...

  4. kafka-伪集群搭建

      一.简介 Apache Kafka是一个快速.可扩展的.高吞吐的.可容错的分布式"发布-订阅"消息系统,使用Scala与Java语言编写,能够将消息从一个端点传递到另一个端点, ...

  5. spring boot:多个filter/多个interceptor/多个aop时设置调用的先后顺序(spring boot 2.3.1)

    一,filter/interceptor/aop生效的先后顺序? 1,filter即过滤器,基于servlet容器,处于最外层, 所以它会最先起作用,最后才停止 说明:filter对所有访问到serv ...

  6. linux mount挂载命令

    [root@localhost src]# mount 查询系统中已经挂载的设备 [root@localhost src]# mount -a 依据配置文件 /etc/fstab的内容,自动挂载

  7. Python之数据类型总结

    1.字符串 2.数字 3.列表 4.元组 5.字典 可变 or 不可变 1:可变:列表.字典 2:不可变:字符串,数字,元组 访问顺序 1.直接访问:数字 2.顺序访问:字符串,列表,元组 3.映射访 ...

  8. ELK6环境搭建

    (一)什么是ELK Stack ELK 到底是什么呢? "ELK"是三个开源项目的首字母缩写,这三个项目分别是:Elasticsearch.Logstash 和 Kibana. E ...

  9. [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构

    [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 目录 [阿里DIN] 深度兴趣网络源码分析 之 整体代码结构 0x00 摘要 0x01 文件简介 0x02 总体架构 0x03 总体代码 0x0 ...

  10. Hibernate关系映射之many-to-many(多对多)

    在表设计中,我们一般都会考虑表与表之间的关系,现在我来介绍一下表与表之间的几种对应关系many-to-many 多对多 比如一个用户可以有多种角色 一种角色可以对用多个不同的用户所以角色和用户之间的关 ...