责任链模式(Chain of Responsibility Pattern) – 设计模式之行为模式:
目录
责任链模式(Chain of Responsibility Pattern)
责任链模式(Chain of Responsibility Pattern)
定义:Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
类图
状态模式通用类图:
责任链模式的核心在“链”上,“链”是由多个处理者ConcreteHandler组成的
例子-审批:
过程:
在工作中,员工出差等需要进行报销审批,审批需要通过主管,经理和总监进行审批。(PS:这里模拟审批状态的一个过程,也没有想到好的状态的一个例子,刚好责任链模式也用这个例子,可以做一个对比。)
类图:
代码:
状态 Approver
public abstract class Approver {
protected String name;// 抽象出审批人的姓名
protected Approver nextApprover;// 下一个审批人,下一个级别领导。
public Approver(String name) {
this.name = name;
}
public Approver setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
return this.nextApprover; // 返回下个审批人
}
public abstract void approve(int amount);
}
抽象的处理者实现三个职责:
1,定义一个请求的处理方法approve,唯一对外开放的方法;
2,定义一个链的编排方法setNext,设置下一个处理者;
3,定义了具体的请求者必须实现的两个方法:定义名字和具体的处理任务。
注意 在责任链模式中一个请求发送到链中后,前一个节点消费部分消息,然后交由后续节点继续处理,最终可以有处理结果也可以没有处理结果
主管 Supervisor
public class Supervisor extends Approver {
public Supervisor(String name) {
super(name);
}
@Override
public void approve(int amount) {
if(amount <= 10000){
System.out.println("主管"+name+"审批通过");
}else{
System.out.println("主管"+name+"无权审批,传送到经理那进行审批");
this.nextApprover.approve(amount);
}
}
}
经理 Manager
public class Manager extends Approver {
public Manager(String name) {
super(name);
}
@Override
public void approve(int amount) {
if(amount <= 50000){
System.out.println("经理"+name+"审批通过");
}else{
System.out.println("经理"+name+"无权审批,传送到总监那进行审批");
this.nextApprover.approve(amount);
}
}
}
总监 Director
public class Director extends Approver {
public Director(String name) {
super(name);
}
@Override
public void approve(int amount) {
if(amount <= 100000){
System.out.println("总监"+name+"审批通过");
}else{
System.out.println("一次性报销金额过大,请分批报销");
}
}
}
中间类
public class ApproveContext {
Approver zhan = new Supervisor("张三");
Approver li = new Manager("李四");
Approver wang = new Director("王五");
public ApproveContext() {
zhan.setNextApprover(li);
li.setNextApprover(wang);
}
public void approveNext(int amount){
zhan.approve(amount);
}
}
测试:
public class ApproveTest {
public static void main(String[] args) {
ApproveContext context = new ApproveContext();
context.approveNext(1000);
System.out.println("=========");
context.approveNext(20000);
System.out.println("=========");
context.approveNext(60000);
System.out.println("=========");
context.approveNext(1100000);
}
}
结果:
主管张三审批通过
=========
主管张三无权审批,传送到经理那进行审批
经理李四审批通过
=========
主管张三无权审批,传送到经理那进行审批
经理李四无权审批,传送到总监那进行审批
总监王五审批通过
=========
主管张三无权审批,传送到经理那进行审批
经理李四无权审批,传送到总监那进行审批
一次性报销金额过大,请分批报销
例子-击鼓传花:
过程:
击鼓传花是一种热闹而又紧张的饮酒游戏,在酒宴上宾客依次坐定,由一人击鼓,开始击鼓时,花束就开始依次传递,鼓声一落,如果花束在谁手里,谁就开始饮酒了。在《红楼梦》中击鼓传花的故事,写一个例子
类图:
代码:
玩家 Player
public abstract class Player {
protected String name; // 击鼓人
private Player nextPlayer; // 传花人
public Player(String name) {
this.name = name;
}
//传入下一个具体的传花人
protected Player setNextPlayer(Player nextPlayer) {
this.nextPlayer = nextPlayer;
return this.nextPlayer;
}
//传递的过程
public abstract void handle(int i);
//传递給下一个
public void nextPlayer(int index) {
if (nextPlayer != null) {
nextPlayer.handle(index);
} else {
System.out.println("这轮游戏结束!");
}
}
}
玩家 PlayerA
public class PlayerA extends Player {
public PlayerA(String name) {
super(name);
}
//传花的过程
@Override
public void handle(int i) {
if (i == 1) {
System.out.println(name+" 喝酒!");
} else {
System.out.println(name+"向下传花!");
this.nextPlayer(i);
}
}
}
子类实现请求的处理,符合单一职责原则,各个实现类只完成一个动作或逻辑,也就是只有一个原因引起类的改变.
玩家 PlayerB
public class PlayerB extends Player {
public PlayerB(String name) {
super(name);
}
//传花的过程
@Override
public void handle(int i) {
if (i == 2) {
System.out.println(name+" 喝酒!");
} else {
System.out.println(name+"向下传花!");
this.nextPlayer(i);
}
}
}
玩家 PlayerC
public class PlayerC extends Player {
public PlayerC(String name) {
super(name);
}
@Override
public void handle(int i) {
if (i == 3) {
System.out.println(name+" 喝酒!");
} else {
System.out.println(name+"向下传花!");
this.nextPlayer(i);
}
}
}
玩家 PlayerD
public class PlayerD extends Player {
public PlayerD(String name) {
super(name);
}
@Override
public void handle(int i) {
if (i == 4) {
System.out.println(name+" 喝酒!");
} else {
System.out.println(name+"向下传花!");
this.nextPlayer(i);
}
}
}
玩家 PlayerE
public class PlayerE extends Player {
public PlayerE(String name) {
super(name);
}
@Override
public void handle(int i) {
if (i == 4) {
System.out.println(name+" 喝酒!");
} else {
System.out.println(name+"向下传花!");
this.nextPlayer(i);
}
}
}
测试:
public class DrumBeaterTest {
public static void main(String args[]) {
PlayerA playerA = new PlayerA("贾母");
PlayerB playerB = new PlayerB("贾赦");
PlayerC playerC = new PlayerC("贾政");
PlayerD playerD = new PlayerD("贾宝玉");
PlayerE playerE = new PlayerE("贾环");
playerA.setNextPlayer(playerB).setNextPlayer(playerC).setNextPlayer(playerD).setNextPlayer(playerE);
int num;
// 玩10轮
for (int i = 0; i < 10; i++) {
System.out.println("========== 第 "+(i+1)+"轮 ===========");
num = (int) (Math.random()*(5)+1);
playerA.handle(num);
}
}
}
结果:
========== 第 1轮 ===========
贾母 喝酒!
========== 第 2轮 ===========
贾母 喝酒!
========== 第 3轮 ===========
贾母 喝酒!
========== 第 4轮 ===========
贾母向下传花!
贾赦向下传花!
贾政向下传花!
贾宝玉 喝酒!
========== 第 5轮 ===========
贾母向下传花!
贾赦 喝酒!
========== 第 6轮 ===========
贾母向下传花!
贾赦 喝酒!
========== 第 7轮 ===========
贾母 喝酒!
========== 第 8轮 ===========
贾母向下传花!
贾赦 喝酒!
========== 第 9轮 ===========
贾母向下传花!
贾赦 喝酒!
========== 第 10轮 ===========
贾母 喝酒!
在实际应用中,一般会有一个封装类对责任模式进行封装,也就是替代Client类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模块的调用,减少模块间的耦合,提高系统的灵活性。
总结:
优点:
1、降低耦合度。它将请求的发送者和接收者解耦。
2、简化了对象。使得对象不需要知道链的结构。
3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
4、增加新的请求处理类很方便。
缺点:
1、不能保证请求一定被接收。
2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3、可能不容易观察运行时的特征,有碍于除错。
使用场景
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
注意事项
链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能