1、引入
在公司中,我们往往可以见到这种场景:我们需要领取材料,要先找到前台,前台告诉我们要到营业窗口,而营业窗口高数我们要到售后不买,而售后部门告诉我们要到资料中心,到了资料中心我们才得以领取到材料。
而这种一级级推卸“责任”的方式,在设计模式中便被称为Chain of Respinsibility模式(责任链模式)。虽然“推卸”听起来有贬义的意思,都是实际上,这种责任推卸可以帮助我们一层层地寻找解决问题的对象。
2、示例
2.1、问题类Trouble
public class Trouble {
private int number;
public Trouble(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
@Override
public String toString() {
return "Trouble{" +
"number=" + number +
'}';
}
}
2.2、问题解决者
2.2.1、抽象父类Support
public abstract class Support {
private String name;
private Support next;
public Support(String name) {
this.name = name;
}
public Support setNext(Support next) {
this.next = next;
return next;
}
public final void support(Trouble trouble) {
if (resolve(trouble)) {
done(trouble);
} else if (next != null) {
next.support(trouble);
} else {
fail(trouble);
}
}
public String toString() {
return "["+name+"]";
}
protected abstract boolean resolve(Trouble trouble);
protected void done(Trouble trouble) {
System.out.println(trouble+" is resolved by "+this+".");
}
protected void fail(Trouble trouble) {
System.out.println(trouble+" cannot be resolved");
}
}
2.2.2、LimitSupport
只能解决序号小于number的问题
public class LimitSupport extends Support{
private int limit;
public LimitSupport(String name, int limit) {
super(name);
this.limit = limit;
}
@Override
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() < limit) {
return true;
}
return false;
}
}
2.2.3、NoSupport
无法解决问题
public class NoSupport extends Support{
public NoSupport(String name) {
super(name);
}
@Override
protected boolean resolve(Trouble trouble) {
return false;
}
}
2.2.4、OddSupport
只能解决序号为奇数的问题
public class OddSupport extends Support{
public OddSupport(String name) {
super(name);
}
@Override
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() % 2 == 1) {
return true;
}
return false;
}
}
2.2.5、SpecialSupport
只能解决特定序号问题
public class SpecialSupport extends Support{
private int number;
public SpecialSupport(String name, int number) {
super(name);
this.number = number;
}
@Override
protected boolean resolve(Trouble trouble) {
if (trouble.getNumber() == number) {
return true;
}
return false;
}
}
2.3、测试
public class Main {
public static void main(String[] args) {
//创建解决者
Support alice = new NoSupport("Alice");
Support bob = new LimitSupport("Bob", 100);
Support charlie = new SpecialSupport("Charlie", 411);
Support diana = new LimitSupport("Diana", 200);
Support elmo = new OddSupport("Elmo");
Support fred = new LimitSupport("Fred", 300);
//设置调用链
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
for (int i = 0; i < 500; i+=33) {
alice.support(new Trouble(i));
}
}
}
运行结果:
3、tips
-
值得注意的是Support类的support()方法被设置为final,代表不可重写,这个final的意义是显著的,如果子类更改了该方法,容易导致调用链断裂。
-
调用链的思想在许多框架、系统内有使用:
- mybatis-plus中:
queryWrapper.like(StringUtils.isNotBlank(name), User::getName, name). ge(beginAge!=null,User::getAge,beginAge). le(endAge!=null,User::getAge,endAge);- 视窗系统中的点击功能,有时在上层按键(或其他)不能解决点击事件时,会将事件交给下层。
-
调用链模式可以通过推卸请求的方式来寻找解决者,然而有时调用链过长会导致较长的延迟,这是在设计时需要权衡的问题。