设计模式-责任链模式
什么是责任链模式
使多个对象都有机会处理请求,从而避免请求的发送者与请求处理者耦合在一起。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
注意:责任链模式也叫职责链模式。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。
角色
- 抽象处理者(Handler): 定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler):实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client):创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
优缺点
优点
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
- 可能不容易观察运行时的特征,有碍于除错。
适用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
实现
我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
抽象的记录器类
/**
* 抽象的记录器类
*/
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
//责任链中的下一个元素
protected AbstractLogger nextLogger;
/**
* 设置下一个元素
*
* @param nextLogger
*/
public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}
/**
* 打印日志
*
* @param level
* @param message
*/
public void logMessage(int level, String message) {
if (this.level <= level) {
write(message);
}
if (nextLogger != null) {
nextLogger.logMessage(level, message);
}
}
/**
* 打印日志
*
* @param message
*/
abstract protected void write(String message);
}
具体处理者
创建扩展了该记录器类的实体类。
控制台日志
/**
* 控制台日志
*/
public class ConsoleLogger extends AbstractLogger {
/**
* 构造方法
* @param level
*/
public ConsoleLogger(int level){
this.level = level;
}
/**
* 打印消息
* @param message
*/
@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}
错误日志
/**
* 错误日志
*/
public class ErrorLogger extends AbstractLogger {
/**
* 构造方法
* @param level
*/
public ErrorLogger(int level){
this.level = level;
}
/**
* 打印日志
* @param message
*/
@Override
protected void write(String message) {
System.out.println("Error Console::Logger: " + message);
}
}
文件日志
/**
* 文件日志
*/
public class FileLogger extends AbstractLogger {
/**
* 构造方法
* @param level
*/
public FileLogger(int level){
this.level = level;
}
/**
* 打印日志
* @param message
*/
@Override
protected void write(String message) {
System.out.println("File::Logger: " + message);
}
}
客户类
创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。
/**
* 责任链模式测试类
*/
public class ChainPatternDemo {
/**
* 获取责任链
*
* @return
*/
private static AbstractLogger getChainOfLoggers() {
//构建错误日志
AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
//构建debug日志
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
//构建info日志
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
//构建责任链
//设置下一级日志为fileLogger
errorLogger.setNextLogger(fileLogger);
//设置下一级日志为consoleLogger
fileLogger.setNextLogger(consoleLogger);
return errorLogger;
}
public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();
loggerChain.logMessage(AbstractLogger.INFO, "打印INFO日志");
loggerChain.logMessage(AbstractLogger.DEBUG,
"打印DEBUG日志");
loggerChain.logMessage(AbstractLogger.ERROR,
"打印ERROR日志");
}
}
输出
Standard Console::Logger: 打印INFO日志
File::Logger: 打印DEBUG日志
Standard Console::Logger: 打印DEBUG日志
Error Console::Logger: 打印ERROR日志
File::Logger: 打印ERROR日志
Standard Console::Logger: 打印ERROR日志
责任链模式的纯与非纯模式
职责链模式可分为纯的职责链模式和不纯的职责链模式两种
纯的职责链模式
一个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个:要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后又将责任向下传递的情况。而且在纯的职责链模式中,要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的情况。在前面的采购单审批实例中应用的是纯的职责链模式。
不纯的职责链模式
在一个不纯的职责链模式中允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收。Java AWT 1.0中的事件处理模型应用的是不纯的职责链模式,其基本原理如下:由于窗口组件(如按钮、文本框等)一般都位于容器组件中,因此当事件发生在某一个组件上时,先通过组件对象的handleEvent()方法将事件传递给相应的事件处理方法,该事件处理方法将处理此事件,然后决定是否将该事件向上一级容器组件传播;上级容器组件在接到事件之后可以继续处理此事件并决定是否继续向上级容器组件传播,如此反复,直到事件到达顶层容器组件为止;如果一直传到最顶层容器仍没有处理方法,则该事件不予处理。每一级组件在接收到事件时,都可以处理此事件,而不论此事件是否在上一级已得到处理,还存在事件未被处理的情况。显然,这就是不纯的职责链模式,早期的Java AWT事件模型(JDK 1.0及更早)中的这种事件处理机制又叫事件浮升(Event Bubbling)机制。从Java.1.1以后,JDK使用观察者模式代替职责链模式来处理事件。目前,在JavaScript中仍然可以使用这种事件浮升机制来进行事件处理。