Logo
Section 1 / 11

责任链模式

本课讲解请求如何在处理器链中逐级传递,直到找到合适的处理器为止。

导入

责任链模式

本课讲解请求如何在处理器链中逐级传递,直到找到合适的处理器为止。

什么是责任链模式?

在责任链模式的实现中,发送者的请求会被沿着一系列处理对象传递,直到其中一个对象处理该请求,或者请求未被处理并最终脱离链。多个对象都有机会处理该请求,从而实现了请求发送者与接收者之间的解耦。

请求方并不知道最终由哪个对象来处理其请求,也不持有对该处理对象的引用;同样地,最终处理请求的对象也不知道请求来自何处。

链中的每个对象都应实现一个共同的父类型,并持有对其后继对象的引用。处理对象可以在运行时动态地添加到链中或从链中移除。

正式定义为:通过将接收对象串联成一条链,并沿链传递请求,直到有对象处理它为止,从而实现请求发送者与接收者的解耦。

通常在请求可由多个对象处理、且事先无法确定具体由哪一个对象处理的情况下应用此模式。

类图

类图包含以下实体:

  • Handler(处理器)
  • Concrete Handler(具体处理器)
  • Client(客户端)
image-253.png
image-253.png

示例

设想一架飞机的驾驶舱。其中运行着一套软件系统,用于向飞行员提示设备故障、发动机温度异常,甚至是火灾等严重情况。假设当某个故障发生时,硬件会向驾驶舱计算机发送一个错误代码,计算机则根据接收到的错误代码采取相应的纠正措施。

我们可以将这些错误代码建模为需要特定组件处理的请求。例如,当发动机出现故障时,可能有一系列递进的补救措施依次尝试解决问题。为了说明目的,假设硬件可以发出“检测到火灾”或“燃油不足”两种请求,每种请求都有对应的具体处理器。首先来看AbstractRequest和AbstractHandler类的定义。

1abstract public class AbstractRequest { 2 3 // 每个请求通过一个整数标识 4 // FireRequest: 1 5 // LowFuelRequest: 2 6 private int requestCode; 7 8 public AbstractRequest(int requestCode) { 9 this.requestCode = requestCode; 10 } 11 12 public int getRequestCode() { 13 return requestCode; 14 } 15} 16 17abstract public class AbstractHandler { 18 19 private AbstractHandler next; 20 21 public AbstractHandler(AbstractHandler next) { 22 this.next = next; 23 } 24 25 public void setNext(AbstractHandler next) { 26 this.next = next; 27 } 28 29 public void handleRequest(AbstractRequest request) { 30 if (next != null) { 31 next.handleRequest(request); 32 } 33 } 34}

接下来是LowFuelRequest和FireDetectedRequest类的简单实现,以及具体处理器FireHandler的实现。由于LowFuelHandler的实现与FireHandler类似,此处省略。

1public class FireDetectedRequest extends AbstractRequest { 2 3 // 火灾请求的代码为1 4 public FireDetectedRequest() { 5 super(1); 6 } 7} 8 9public class LowFuelRequest extends AbstractRequest { 10 11 // 燃油不足请求的代码为2 12 public LowFuelRequest() { 13 super(2); 14 } 15} 16 17public class FireHandler extends AbstractHandler { 18 19 // 只处理请求代码为1的请求 20 private static int code = 1; 21 22 public FireHandler(AbstractHandler successor) { 23 super(successor); 24 } 25 26 @Override 27 public void handleRequest(AbstractRequest request) { 28 if (code == request.getRequestCode()) { 29 // 在此处处理请求 30 } else { 31 // 如果当前处理器不处理此类请求, 32 // 则调用父类方法将请求转发给下一个处理器 33 super.handleRequest(request); 34 } 35 } 36}

最后,客户端代码展示了这些类之间的交互方式。

1public class Client { 2 3 public void main() { 4 5 // 构建如下链结构: 6 // FireHandler --> LowFuelHandler --> null 7 // 链中仅包含两个处理器,fireHandler位于链首 8 AbstractHandler lowFuelHandler = new LowFuelHandler(null); 9 FireHandler fireHandler = new FireHandler(lowFuelHandler); 10 11 // 创建一个表示燃油不足的紧急请求 12 LowFuelRequest lowFuelRequest = new LowFuelRequest(); 13 14 // 让链来处理该请求 15 fireHandler.handleRequest(lowFuelRequest); 16 } 17}

注意,在本示例中,请求会沿着链传递,直到遇到能够处理它的处理器为止。链还定义了请求被处理的顺序:如果有多个处理器都能处理同一请求,则排在链前面的处理器将优先处理。

其他示例

  • 前端开发者可能熟悉 JavaScript 中事件处理器的工作机制。当点击按钮且已实现事件处理函数时,事件会被处理,并可通过阻止冒泡防止传递给父元素的事件处理器。这些事件处理器可视为形成了一条从最内层 HTML 元素(叶子节点)到根元素的链。需要注意的是,浏览器也支持事件捕获机制,即事件从最外层元素向内传播至目标元素。与标准责任链模式略有不同的是,是否阻止事件继续传播是由开发者可选控制的。

  • 另一个例子是 Web 请求经过多个过滤器的过程。javax.servlet.Filter.doFilter() 就遵循了责任链模式。每当客户端请求链末端资源时,容器都会调用 Filter 的 doFilter 方法。传入该方法的 FilterChain 对象允许当前过滤器将请求和响应传递给链中的下一个实体。

注意事项

  • 为了构建处理器链,各个对象可能需要保存对其后继对象的引用。但在某些情况下,这种链接关系可能已经存在,例如在树形结构的组合模式中。

结构型模式

观察者模式