行为型模式(八) 职责链模式(Chain of Responsibility)
一、动机(Motivate)
在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合。如何使请求的发送者不需要指定具体的接受者,让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。
二、意图(Intent)
避免请求发送者与接收者耦合在一起,让多个对象都有可能接受请求,将这些对象连接成一条链,并且沿着这条链传递请求,知道有对象处理它为止。 ——《设计模式》GoF
三、结构图(Structure)

四、模式的组成
可以看出,在职责链模式的结构图有以下角色:
(1)、抽象处理者角色(Handler):抽象处理者定义了一个处理请求的接口,它一般设计为抽象类,由于不同的具体处理者处理请求的方式不同,因此在其中定义了抽象请求处理方法。因为每一个处理者的下家还是一个处理者,因此在抽象处理者中定义了一个自类型的对象,作为其对下家的引用。通过该引用,处理者可以连成一条链。
(2)、具体处理者角色(ConcreteHandler):具体处理者是抽象处理者的子类,它可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中下一个对象,以便请求的转发。
五、职责链模式的代码实现
在现实生活中,职责链模式的例子也是很多的,例如:公司的请假流程就是一个很好的职责链模式的例子,如果请假半天,只要告诉本部门经理就可以了;如果请假7天或者以上必须人事总监批准;如果请假15天以上,那就要经过总裁批准了。还有类似的例子就是采购的流程,其流程也是职责链模式很好的体现,采购的金额不同,需要批准的人员也不同,比如:部门采购1万元的纸品,只要部门领导签批就可以,如果要采购大于1万小于5万的物品,那就需要财务经理签批了,如果采购30万的原材料或者物品,那就需要总裁或者类似角色才能审批了。接下来我们就以采购的实例来说明职责链模式。实现代码如下:
static void Main(string[] args)
{
PurchaseRequest requestDao = new PurchaseRequest(8000.0, "单刀5把");
PurchaseRequest requestHuaJi = new PurchaseRequest(10000.0, "10把方天画戟");
PurchaseRequest requestJian = new PurchaseRequest(80000.0, "5把金丝龙鳞闪电劈"); Approver manager = new Manager("黄飞鸿");
Approver financial = new FinancialManager("黄麒英");
Approver ceo = new CEO("十三姨"); // 设置职责链
manager.NextApprover = financial;
financial.NextApprover = ceo; // 处理请求
manager.ProcessRequest(requestDao);
manager.ProcessRequest(requestHuaJi);
manager.ProcessRequest(requestJian); }
// 采购请求
public sealed class PurchaseRequest
{
// 金额
public double Amount { get; set; } // 产品名字
public string ProductName { get; set; } public PurchaseRequest(double amount, string productName)
{
Amount = amount;
ProductName = productName;
}
} //抽象审批人,Handler---相当于“抽象处理者角色”
public abstract class Approver
{
//下一位审批人,由此形成一条链
public Approver NextApprover { get; set; } //审批人的名称
public string Name { get; set; } public Approver(string name)
{
this.Name = name;
} //处理请求
public abstract void ProcessRequest(PurchaseRequest request);
} //部门经理----相当于“具体处理者角色” ConcreteHandler
public sealed class Manager : Approver
{
public Manager(string name) : base(name) { } public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount <= 10000.0)
{
Console.WriteLine("{0} 部门经理批准了对原材料{1}的采购计划!", this.Name, request.ProductName);
}
else if (NextApprover != null)
{
NextApprover.ProcessRequest(request);
}
}
} //财务经理---相当于“具体处理者角色”ConcreteHandler
public sealed class FinancialManager : Approver
{
public FinancialManager(string name) : base(name) { } public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount > 10000.0 && request.Amount <= 50000.0)
{
Console.WriteLine("{0} 财务经理批准了对原材料{1}的采购计划!", this.Name, request.ProductName);
}
else if (NextApprover != null)
{
NextApprover.ProcessRequest(request);
}
}
} //总裁---相当于“具体处理者角色” ConcreteHandler
public sealed class CEO : Approver
{
public CEO(string name) : base(name) { } public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount > 50000.0 && request.Amount < 300000.0)
{
Console.WriteLine("{0} 总裁批准了对原材料 {1} 的采购计划!", this.Name, request.ProductName);
}
else
{
Console.WriteLine("这个采购计划的金额比较大,需要一次董事会会议讨论才能决定!");
}
}
}
六、职责链模式的实现要点:
Chain of Responsibility模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只有一个”,只有这时候请求发送者与接受者的耦合才有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
应用了Chain of Responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
当我们要新增一个DHandler处理请求,就不需再改原来的代码了,遵从了开放封闭原则。这样我们的程序就更赋予变化,更有变化的抵抗力。Handler类本身继承自BaseHandler类型,又包含了一个BaseHandler类型的对象,这点类似Decorator模式。
如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。
1、职责链模式的主要优点有:
1】、降低耦合度:职责链模式使得一个对象无需知道是其他哪一个对象处理其请求。对象仅需知道该请求会被处理即可,接受者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构,有客户端负责链的创建。
2】、可简化对象的相互连接:接受者对象仅需维持一个指向其后继者的引用,而不需维持它对所有的候选处理者的引用。
3】、增强给对象指派职责的灵活性:在给对象分派职责时,职责链可以给我们带来更多的灵活性。可以通过在运行时对该连进行动态的增加或修改处理一个请求的职责。
4】、增加新的请求处理类很方便:在系统中增加一个新的请求处理者无需修改原有系统的代码,只需要在客户端重新建链即可,从这一点看来是符合“开闭原则”的。
2、职责链模式的主要缺点有:
1】、在找到正确的处理对象之前,所有的条件判定都要执行一遍,当责任链过长时,可能会引起性能的问题。
2】、可能导致某个请求不被处理。
3】、客户端需要组装这个链条,耦合了客户端和链条的组成结构,可以把这个在客户端的组合动作提到外面,通过配置来做,会更好点。
3、在下面的情况下可以考虑使用职责链模式:
1】、一个系统的审批需要多个对象才能完成处理的情况下,例如请假系统等。
2】、代码中存在多个if-else语句的情况下,此时可以考虑使用责任链模式来对代码进行重构
3】、有多个对象可以处理同一个请求,具体哪个对象处理该请求有运行时刻自动确定。客户端只需将请求提交到链上,无须关心请求的处理对象是谁以及它是如何处理的。
4】、不明确指定接受者的情况下,向多个对象中的一个提交一个请求。请求的发送者与请求者解耦,请求将沿着链进行传递,寻求响应的处理者。
5】、可动态指定一组对象处理请求。客户端可以动态创建职责链来处理请求,还可以动态改变链中处理者之间的先后次序
七、.NET 职责链模式的实现
这个模式在Net框架中的实现不多,我感觉这个模式的使用场景更多的是在业务系统总才会有更大的用处。这种模式在处理UI的消息时很常用,但实际上Windows消息循环还是硬编码的结构。因为效率上的考虑,Windows消息循环是哪个对象有一个请求,则直接到达处理函数的地址。如果链条上的对象多了,而真正处理的函数在链条后部分,效率会很低下。因此我们在使用这种模式的时候更适合业务流程,即对性能要求不是特别高的情况更加常用。
行为型模式(八) 职责链模式(Chain of Responsibility)的更多相关文章
- 设计模式的征途—14.职责链(Chain of Responsibility)模式
相信大家都玩过类似于“斗地主”的纸牌游戏,某人出牌给他的下家,下家看看手中的牌,如果要不起,则将出牌请求转发给他的下家,其下家再进行判断.一个循环下来,如果其他人都要不起该牌,则最初的出牌者可以打出新 ...
- js设计模式——6.模板方法模式与职责链模式
js设计模式——6.模板方法模式与职责链模式 职责链模式
- 行为型设计模式之职责链模式(Chain of Responsibility)
结构 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 适用性 有多个的对象可以处理一个请求,哪个 ...
- 设计模式24---设计模式之职责链模式(Chain of Responsibility)(行为型)
1.职责链模式讲解 1.1职责链定义 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 1.2职责链模式 ...
- 十四、职责链(Chain of Responsibility)模式--行为型模式(Behavioral Pattern)
职责链模式是一种对象的行为模式[GOF95].在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知 ...
- 设计模式---数据结构模式之职责链模式(Chain of Responsibility)
一:概念 职责链模式(CoR,Chain of Responsibility)是行为模式之一,该模式构造一系列分别担当不同的职责的类的对象来共同完成一个任务,这些类的对象之间像链条一样紧密相连,所以被 ...
- 行为类模式(一):职责链(Chain Of Responsibility)
定义 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系. 将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. UML 优点 将请求的发送者和接收者解耦 可以 ...
- 状态模式、职责链模式——省去if-else的繁琐结构
小时候写日记都是这么写的:上午七点起床,八点之前洗脸刷牙吃早饭,十二点之前好好上课,中午一点,吃午饭,下午两点到六点,上课,下课,找请假,明天妈妈要带我去姥姥家,九点之前,看动画片,九点钟,收拾去姥姥 ...
- JavaScript---设计模式之职责链模式
概念 职责链模式是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止. 链中收到请求的对象要么亲自处理它,要 ...
随机推荐
- sql server版本特性简介、版本介绍简介
1.SQL Server 版本简介 1.1.sql server的版本信息 年 代 版 本 大版本号 1993年 SQL Server for Windows NT 4.21 1994年 ...
- c++模板使用及常见问题
一.为什么使用模板?? 使用模板的目的是为了避免重复声明和定义一系列基本功能相同的函数或者类,其区别因传入参数的不同而产生不同类型的数据,其基本工作过程都是一致的! 二.调用模板函数产生不明确问题 ( ...
- java.IO.EOFException异常
错误代码为: 43 boolean booleanResult = dis.readBoolean();//dis为DateInputStream的实例 44 System.out.println(b ...
- leetcode 罗马数字和数字的互相转换
不知哪个大佬说过: 关于字符串的题都可以用指针或哈希解决. 罗马数字转数字: 思想: 我们能观察到规律: 一般情况下,表示大的字母在前,小字母在后; 特殊情况下,小字母会在大字母之前,但是相应的,得到 ...
- pyenv基本使用
pyenv使用 1.安装: git clone https://github.com/pyenv/pyenv.git 2.配置pyenv环境变量 echo 'export PYENV_ROOT=&qu ...
- python基础_mysql建表、编辑、删除、查询、更新
1.建一张学生表 包含(id,name,age,sex)2.增加四条数据3.查询表中sex为男的数据4.删除id =3的数据,5.将sex为女的,修改为男 create: CREATE TABLE d ...
- Jupyter交互式工具安装使用
Jupyter交互式工具安装使用 Jupyter Notebook(此前被称为IPython notebook)是一个交互式笔记本,支持运行 40 多种编程语言. 文档:https://jupyter ...
- java 堆 排序学习
/** * <html> * <body> * <P> Copyright 1994 JsonInternational</p> * <p> ...
- NEST routing timeout scroll
/// <summary> /// PUT /employee/employee/9e5e50da-7740-488e-bee2-b24951435691?routing=test_rou ...
- 使用 Code First 数据库迁移
当 Entity Framework Code First 的数据模型发生改变时,默认会引发一个System.InvalidOperationException 的异常.解决方法是使用DropCrea ...