责任链模式
介绍
因为链式结构具有很好的灵活性,将其应用于编程领域:将每个节点当作一个对象,没个对象有不同的处理逻辑,将一个从请求从链式的首端发出,沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止
定义
是很多对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有对象处理它为之。
使用场景
- 多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定
- 在请求处理者不明确的情况下,向多个对象中的一个提交一个请求
- 需要动态指定一组对象处理请求
通用模版
抽象处理者Handler
public abstract class Handler { //下一个节点的处理者 protected Handler successor; //请求处理 public abstract void handleRequest(String condition); }抽象处理者需要子类继承实现其具体处理的逻辑,可以借由父类的successor赋值当前节点下一个节点处理者
具体处理者1
public class ConcreteHandler1 extends Handler { @Override public void handleRequest(String condition) { if (condition.equals("ConcreteHandler1")){ System.out.println("ConcreteHandler1 handled"); return; }else { if (successor!=null) successor.handleRequest(condition); } } }具体处理者继承父类处理者,实现具体处理的逻辑。如果不是自己能处理的则转交下一个节点继续处理,对于下一节点的设置需要在Client中指明。
具体处理者2
public class ConcreteHandler2 extends Handler { @Override public void handleRequest(String condition) { if (condition.equals("ConcreteHandler2")){ System.out.println("ConcreteHandler2 handled"); return; }else { if (successor!=null){ successor.handleRequest(condition); } } } }具体处理者2得到节点1发来消息继续处理(节点一指明节点2为ConcreteHandler2),如果符合自己处理的情况直接处理,否则继续向下一个节点传递。
Client客户端
public class Client { public static void main(String args[]){ // 实例两个具体处理者 ConcreteHandler1 concreteHandler1 = new ConcreteHandler1(); ConcreteHandler2 concreteHandler2 = new ConcreteHandler2(); // 指明调用者下一个节点 concreteHandler1.successor = concreteHandler2; concreteHandler2.successor = concreteHandler1; // 链首开始调用 concreteHandler1.handleRequest("ConcreteHandler2"); } }客户端内首先实例两个具体的处理者,对处理者之间指明下一个节点,方便调用时可以向下传递。这里可以看出两者互为下一个节点。最后从链首开始调用“ConcreteHandler2”。
LOG如下
ConcreteHandler2 handled
ConcreteHandler1直接调用参数名“ConcreteHandler2”,内部逻辑判断不能直接处理,转交下一节点ConcreteHandler2来调用,ConcreteHandler2能够处理此逻辑,直接打印完成本次链式调用。
升级模版
由于责任链中的请求和对应的处理规则是不尽相同的,在这种情况下封装请求,同时对请求的处理规则也进行封装,代码如下:
抽象出处理者
public abstract class AbstractHandler {
//下一节点的处理者对象 protected AbstractHandler nextHandler; //请求处理 public final void handleRequest(AbstractRequest request){ if (request.getRequestLevel() == getHandleLevel()){ handle(request); }else { if (nextHandler!=null){ nextHandler.handleRequest(request); }else System.out.println("All of handler can not handle the request!"); } } //由子类实现,具体处理方式 public abstract void handle(AbstractRequest request); //由子类实现,获取处理者对象级别 public abstract int getHandleLevel();}
同上抽象处理者相似,除了成员变量下一节点,对处理的逻辑进行一定的的封装。首先检测当前处理级别是否对应,对应直接由虚拟方法子类具体实现处理,否则判断节点并交予下一节点处理,不能处理便直接打印处理。
同时处理中使用了封装的AbstractRequest请求,下面介绍。具体处理者1,2,3
public class Handler1 extends AbstractHandler { @Override public void handle(AbstractRequest request) { System.out.println("Handler1 handle the request: "+ request.getRequestLevel()); } @Override public int getHandleLevel() { return 1; } }具体处理者直接处理哭啼逻辑,父类已经进行了相关的等级判断,这是共有的逻辑可以封装在父类中,同时需要返回当前处理的等级,以满足父类判断的需要。
抽象请求者
public abstract class AbstractRequest { //处理对象 private Object obj; public AbstractRequest(Object obj) { this.obj = obj; } public Object getContext(){ return obj; } public abstract int getRequestLevel(); }抽象请求者封装抽象处理者需要的等级判断条件,需要子类实现具体请求的等级。
具体请求者1,2,3
public class Request1 extends AbstractRequest { public Request1(Object obj) { super(obj); } @Override public int getRequestLevel() { return 1; } }方法相对简单,直接返回当前对象对应的请求等级。
Client端实现
public class Main { public static void main(String args[]) { //构造三个处理者对象 Handler1 handler1 = new Handler1(); Handler2 handler2 = new Handler2(); Handler3 handler3 = new Handler3();
//设置当前处理者对象的下一个处理者
handler1.nextHandler = handler2;
handler2.nextHandler = handler3;
//构造3个请求者
Request1 request1 = new Request1("Request1");
Request2 request2 = new Request2("Request2");
Request3 request3 = new Request3("Request3");
//从链式的首端发起请求
handler1.handleRequest(request1);
handler1.handleRequest(request2);
handler1.handleRequest(request3);
}
}
Client端首先构建三个处理者,并设置对应的节点关系。之后实例三个具体的请求者。链式调用从链首开始,调用Handlelr1处理请求,让其依次处理请求1,2,3,对于请求1,自己可以直接处理,对于请求2,3,处理者1不能直接处理,只能向下节点传递,有下面节点处理。
LOG分析
Handler1 handle the request: 1 Handler2 handle the requset: 2 Handler3 handle the request: 3
从log我们可以看出:
- 具体处理者1可以直接处理请求1;
- 对于请求2,虽然使用处理者1来调用,但是可以通过链式传递到达节点2,由处理者2处理;
- 对于请求3,处理者1不满足处理条件,通过链转下一节点到处理者2,处理者2仍然不满足处理条件,再通过链转交节点3,处理者3满足条件直接处理。
总结
责任链模式与状态者模式有相似之处,但也有区别:
职责链模式中的各个对象并不指定其下一个处理的对象到底是谁,只有在客户端才设定某个类型的链条,请求发出后穿越链条,直到被某个职责类处理或者链条结束。设计思路是把各个业务逻辑判断封装到不同职责类,且携带下一个职责的对应引用,但不像状态模式那样需要明确知道这个引用指向谁,而是在环境类设置链接方式或者过程。使用时,向链的第一个子类的执行方法传递参数就可以。客户端去通过环境类调用责任链,全自动运转起来。
状态模式是让各个状态对象自己知道其下一个处理的对象是谁,即在编译时便设定。设计思路是把逻辑判断转移到各个State类的内部实现(相当于If,else If),执行时客户端通过调用环境—Context类的方法来间接执行状态类的行为,客户端不直接和状态交互。
优缺点
- 优点显而易见,可以对请求者和处理者关系解耦,提高代码灵活性。
- 最大的缺点是对链中请求处理者的遍历,处理处理者太多,遍历必然会影响性能,特别是在一些递归调用中。