设计模式学习笔记(十六):职责链模式
1 概述
1.1 引言
很多情况下,一个软件系统中可以处理某个请求的对象不知一个,比如采购单的审批,主任,副董事长,董事长,董事会都可以处理采购单,他们可以构成一条处理采购单的链式结构,采购单沿着这条链进行传递,这条链就叫职责链。
职责链可以是一条直线,一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。链上的每一个对象都是请求处理者,职责链模式可以将请求的处理者组织成一条链,并让请求沿着链传递,由链上的处理者对请求进行处理,客户端无须关系请求的处理细节以及具体的传递,只需要将请求发送到链上即可,实现请求发送者以及请求处理者的解耦。
1.2 定义
职责链模式:避免将请求发送者与接收者耦合在一起,让多个对象都有机会接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
职责链模式是一种行为型模式。
1.3 结构图
1.4 角色
Handler
(抽象处理者):定义一个处理请求的接口,一般为抽象类。因为每一个处理者的下家还是一个处理者,因此在抽象处理者中定义了一个抽象处理者的对象作为对下一个处理者的引用,通过该引用,处理者可以连成一条链ConcreteHandler
(具体处理者):抽象处理者的子类,实现具体处理方法,在处理前需要判断是否具有处理权限,如果拥有权限则处理,没有则转发到下一个处理者
2 典型实现
2.1 步骤
- 定义抽象处理者:定义处理请求接口以及定义一个抽象处理者成员,作为下一个处理者的引用,一般为了让具体处理者方便调用,定义为
protected
- 定义具体处理者:处理/转发请求,处理请求前先判断是否具有权限 ,拥有则处理请求,否则转发请求
- 客户端创建职责链:职责链模式并不创建职责链,职责链交由客户端创建,根据实际需要定义职责链顺序
2.2 抽象处理者
abstract class Handler
{
protected Handler successor;
public void setSuccessor(Handler successor)
{
this.successor = successor;
}
public abstract void handleRequest(int num);
}
拥有一个设置下一处理者的对象,可以通过setter注入,同时声明抽象处理方法。
2.3 具体处理者
class ConcreteHandler1 extends Handler
{
@Override
public void handleRequest(int num)
{
if(num < 10)
{
System.out.println("处理小于10的数字:"+num);
}
else
successor.handleRequest(num);
}
}
class ConcreteHandler2 extends Handler
{
@Override
public void handleRequest(int num)
{
if(num < 20)
{
System.out.println("处理大于等于10且小于20的数字:"+num);
}
else
successor.handleRequest(num);
}
}
class ConcreteHandler3 extends Handler
{
@Override
public void handleRequest(int num)
{
if(num < 30)
{
System.out.println("处理大于等于20且小于30的数字:"+num);
}
else
successor.handleRequest(num);
}
}
继承抽象处理者,首先判断是否拥有权限处理(这里是一个简单的if判断),如果有就处理,没有的话通过protected
对象,也就是转发给下一个处理者处理。
2.4 客户端
public static void main(String[] args)
{
Handler handler = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
handler.setSuccessor(handler2);
handler2.setSuccessor(handler3);
handler.handleRequest(3);
handler.handleRequest(15);
handler.handleRequest(22);
}
客户端针对抽象处理者编程,需要创建每一个具体处理者对象,并且自定义职责链:
handler.setSuccessor(handler2);
handler2.setSuccessor(handler3);
接着调用对应的处理者处理即可。
3 实例
设计一个采购单审批系统,分级进行,根据金额不同由不同层次的人员审批,主任可以审批5w以下的采购单,副董事长可以审批5w-10w,董事长可以审批10w-50w,50w以上需要由董事会审批,使用职责链模式设计该系统。
设计如下:
- 抽象处理者:
Approver
- 具体处理者:
Director
+VicePresident
+President
+Congress
- 采购单请求类:
PurchaseRequest
代码如下:
//抽象处理者
abstract class Approver
{
protected Approver successor;
public void setSuccessor(Approver successor) {
this.successor = successor;
}
public abstract void processRequest(PurchaseRequest request);
}
//具体处理者:主任
class Director extends Approver
{
@Override
public void processRequest(PurchaseRequest request)
{
if(request.getAmount() < 50000)
System.out.println("主任审批一笔\n金额为"+request.getAmount()+"\nid为"+request.getId()+"\n的采购单\n");
else
successor.processRequest(request);
}
}
//具体处理者:副董事长
class VicePresident extends Approver
{
@Override
public void processRequest(PurchaseRequest request)
{
if(request.getAmount() < 100000)
System.out.println("副董事长审批一笔\n金额为"+request.getAmount()+"\nid为"+request.getId()+"\n的采购单\n");
else
successor.processRequest(request);
}
}
//具体处理者:董事长
class President extends Approver
{
@Override
public void processRequest(PurchaseRequest request)
{
if(request.getAmount() < 500000)
System.out.println("董事长审批一笔\n金额为"+request.getAmount()+"\nid为"+request.getId()+"\n的采购单\n");
else
successor.processRequest(request);
}
}
//具体处理者:董事会
class Congress extends Approver
{
@Override
public void processRequest(PurchaseRequest request)
{
System.out.println("董事会审批一笔\n金额为"+request.getAmount()+"\nid为"+request.getId()+"\n的采购单\n");
}
}
//请求类:采购单
class PurchaseRequest
{
private double amount;
private String id;
private static final String STR = "xcnvj232cvm";
private static final Random random = new Random();
public PurchaseRequest(double amount)
{
this.amount = amount;
//简易的随机字符串
this.id = STR.substring(0,random.nextInt(STR.length()-1)+1).repeat(random.nextInt(3)+2);
}
public double getAmount() {
return this.amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
}
测试:
public static void main(String[] args)
{
Approver director = new Director();
Approver vicePresident = new VicePresident();
Approver president = new President();
Approver congress = new Congress();
director.setSuccessor(vicePresident);
vicePresident.setSuccessor(president);
president.setSuccessor(congress);
director.processRequest(new PurchaseRequest(12345));
director.processRequest(new PurchaseRequest(54321));
director.processRequest(new PurchaseRequest(123456));
director.processRequest(new PurchaseRequest(654321));
}
输出如下:
4 分类
职责链模式可以分为纯的职责链模式与不纯的职责链模式。
4.1 纯的职责链模式
一个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个,要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后又将责任向下传递的情况。
而且在纯的职责链模式中,要求一个请求必须被某一个处理者对象接收,不能出现某个请求未被任何一个处理者对象处理的情况,比如前面的采购单例子。
4.2 不纯的职责链模式
在一个不纯的职责链模式中,允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其猴戏处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收。
在Java AWT 1.0中的事件处理模型应用就是不纯的职责链模式,基本原理如下:由于窗口组件一般位于容器组件中,当事件发生在窗口组件上时,先通过组件对象的handleEvent()
方法传递给相应的事件处理方法,该事件处理方法将处理该事件,然后决定是否将该事件向上一级容器组件传播,上级容器组件在接到事件之后可以继续处理此事件并决定是否继续向上级容器组件传播,直到顶层容器组件为止。如果一直都没有处理方法则不处理该事件。
这种事件处理机制又叫事件浮升机制,JDK1.1后使用观察者模式来代理职责链模式处理事件。
5 主要优点
- 降低耦合:职责链模式使得一个对象无须知道是其他哪一个对象处理请求,对象仅需知道请求会被处理即可,接收者和发送者都没有对方明确信息,且链中对象不需要知道链的结构,由客户端负责链的创建,降低了系统耦合度
- 简化对象连接:请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有候选处理者的引用,可简化对象的相互连接
- 灵活的职责链:可以在运行时对链进行动态增加或者修改
- 符合OCP:系统增加一个新的具体处理者时无须修改源码,只需要客户端重建职责链,符合OCP
6 主要缺点
- 请求可能得不到处理:由于一个请求没有明确的接收者,因此请求不一定会被处理,也有可能因为职责链配置错误而得不到处理
- 性能受到影响:对于较长的职责链,请求的处理可能涉及多个处理对象,系统性能会受到一定影响,而且代码调试时可能不方便
- 死循环:如果职责链不当,可能会导致死循环调用
7 适用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的
- 在不明确指定接收者的情况下,向多个对象的一个提交一个请求
- 可动态指定一组对象处理请求,客户端可以动态创建职责链来处理请求,还可以改变链中处理请求以及处理者之间的先后次序