(一段时间没更新了,坚持了100天考研^^)
14.1 Chain of Responsibility (责任链)模式
“推卸责任” 有贬义的意思,但是有时候也需要 “推卸责任”的情况。例如,当外部请求程序进行某个处理,但程序暂时无法之间决定由哪个对象负责处理时,就需要推卸责任。这种情况下,我们可以考虑将多个对象组成一条责任链,然后按照他们在责任链上的顺序一个个地找出到底谁来负责处理。
这种模式称为 Chain of Responsibility 模式。我们可以将它想象为推卸责任的结构。这种模式可以弱化 “请求方” 和 “处理方” 之间的关联关系。让双方各自都成为可独立复用的组件。此外,程序还可以应对其他请求,如根据情况不同,负责处理的对象也会发生变化的这种需求。
当一个人被要求做什么事情时,如果他可以做就自己做,如果不能做就将 “要求” 转给另外一个人。下一个人如果可以自己处理,就自己做;如果也不能自己处理,就再转给另外一个人…这就是 Chain of Responsibility 模式。
14.2 示例程序
下面我们来看看使用了 Chain of Responsibility 模式的示例程序。
|| Trouble 类
Trouble 类是表示发生的问题的类。number 是问题的编号。通过 getNumber 方法可以获取问题编号。
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 + "]";
}
}
|| Support 类
Support 类是用来解决问题的抽象类,它是职责链上的对象。
next 字段中指定了要推卸给的对象。
resolve 方法是需要子类去实现的抽象方法(模版方法模式)。如果 resolve 返回 true,则表示问题已经被处理,如果返回 false 表示问题还没有被处理,需要推卸给下一个对象。
support 方法会调用 resolve 方法,如果 resolve 方法返回 false,则 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 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.");
}
@Override
public String toString() {
return "[" + name + "]";
}
}
|| NoSupport 类
NoSupport 类是 Support 的子类。NoSupport 类的 resolve 方法总是返回 false。即它是一个永远 “不解决问题” 的类。
/**
* 什么也不做处理的类,只返回 false
*/
public class NoSupport extends Support {
public NoSupport(String name) {
super(name);
}
@Override
public boolean resolve(Trouble trouble) {
return false;
}
}
|| LimitSupport 类
LimitSupport 类解决编号小于 limit 值的问题。resolve 方法在判断编号小于 limit 后,只是简单的返回 true,但实际上这里应该是解决问题的代码,不过省略了。
/**
* 只处理编号小于 limit 的问题
*/
public class LimitSupport extends Support {
private int limit;
public LimitSupport(String name, int limit) {
super(name);
this.limit = limit;
}
@Override
public boolean resolve(Trouble trouble) {
if (trouble.getNumber() < limit) {
// ...
return true;
}
return false;
}
}
|| OddSupport 类
OddSupport 类解决奇数编号的问题。
/**
* 只解决编号为奇数的问题.
*/
public class OddSupport extends Support {
public OddSupport(String name) {
super(name);
}
@Override
public boolean resolve(Trouble trouble) {
if (trouble.getNumber() % 2 == 1) {
//...
return true;
}
return false;
}
}
|| SpecialSupport 类
SpecialSupport 类只解决指定编号的问题。
/**
* 只解决指定编号的问题.
*/
public class SpecialSupport extends Support{
private int number;
public SpecialSupport(String name, int number) {
super(name);
this.number = number;
}
@Override
public boolean resolve(Trouble trouble) {
if (number == trouble.getNumber()) {
// ...
return true;
}
return false;
}
}
|| Main 类
Main 类首先生成 Alice 至 Fred 等6个解决问题的实例。接下来,Main 类调用 setNext 方法将 Alice 至 Fred 这6个实例串联在职责链上。之后 Main 类逐个生成问题,并将它们传递给 alice,然后显示最终谁解决了该问题。
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", 429);
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));
}
}
}
运行结果:
[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].
实际上,每个 Support 在调用下一个 Support 的 support 方法之前,都会先调用自身的 resolve 方法。
14.3 Chain of Responsibility 模式中的登场角色
在 Chain of Responsibility 模式中有以下登场角色。
◆ Handler(处理者)
Handle 角色定义了处理请求的接口(API)。Handler 角色知道 “下一个处理者” 是谁,如果自己无法处理请求,它会将请求转给 “下一个处理者”。当然,“下一个处理者” 也是 Handler 角色。
◆ ConcreteHandler(具体的处理者)
ConcreteHandler 角色是处理请求的具体角色。在示例程序中,由 NoSupport、LimitSupport、OddSupport、SpecialSupport 等各个类扮演此角色。
** ◆ Client(请求者)**
Client 角色是向第一个 ConcreteHandler 角色发生请求的角色。在示例程序中,由 Main 类扮演此角色。
14.4 拓展思路的要点
|| 弱化了发出请求的人和处理请求的人之间的关系
Chain of Responsibility 模式的最大优点就在于它弱化了发出请求的人和处理请求的人(ConcreteHandler)之间的关系。Client 角色向第一个 ConcreteHandler 角色发出请求,然后请求会在职责链中传播,直到某个 ConcreteHandler 角色处理该请求。
如果不使用该模式,那么就必须有某个伟大的角色知道“谁该处理什么请求”,这有点类似中央集权制。而让 “发出请求的人” 知道 “谁该处理请求” 并不明智,因为如果发出请求的人不得不知道请求的人各自的责任分担,就会降低其作为可复用组件的独立性。
在示例程序中,为了简单起见,我们让 Client 角色的 Main 类负责串联起 ConcreteHandler 的职责链。
|| 可以动态地改变职责链
在示例程序中,问题的解决是按照从 Alice 到 Fred 的固定顺序进行处理的。但是,我们还需要考虑负责处理的各个 ConcreteHandler 角色之间的关系可能会发生变化的情况。如果使用 Chain of Responsibility 模式,通过委托推卸责任,就可以根据情况变化动态地重组责任链。
如果不使用该模式,而是在程序中固定写明 “某个请求需要谁处理” 这样的对应关系,那么很难在程序运行中去改变请求的处理者。
在视窗系统中,用户有时需要可以自由地在视窗中添加控件(按钮或文本输入框等)。这时,Chain of Responsibility 模式就有了用武之地。
|| 专注于自己的工作
每个 ConcreteHandler 角色都专注于自己所负责的处理。当自己无法处理时,ConcreteHandler 角色就会干脆地对下一个处理这说一句 “交给你了”,然后将请求转出去。这样,每个 ConcreteHandler 角色就能只处理它应该负责的请求了。
|| 推卸请求会导致处理延迟吗
使用 Chain of Responsibility 模式可以推卸请求,直至找到合适的处理请求的对象,确实提高了程序的灵活性,但是难道不会导致处理延迟吗?
确实如此,与 “事先确定哪个对象负责什么样的处理,当接收到请求时,立即让相应的对象去处理请求” 相比,使用该模式确实导致处理请求发生了延迟。
不过,这是一个需要权衡的问题。如果请求和处理者之间的关系是确定的,而且需要非常快的处理速度时,不是 Chain of Responsibibliy 模式会更换。