责任链模式(Chain of Responsibility)

击鼓传花,层层审批,解决各种 if else 判断的复杂业务情况

责任链模式

概述

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。

通常情况下每个接受者包含对另一个接收者的引用,意思就是这个接受者处理不了了,扔给下个人处理,或者这个人处理过了,给下一个人审批。

责任链就是一条链路,传入一个参数,按照事先排好的链路顺序,依次判断处理。

使用场景

有多个对象处理同一个请求的情况,具体哪个对象处理由运行时刻决定(可以理解为需要多次的 if 判断才能决定走哪个分支)

为什么要用责任链模式?直接用 if 判断不行吗?

简单情况下,确实没必要用责任链模式,但是如果 if 判断很多种情况,而且每个分支里处理的代码也有很多呢?必然会难以维护

这种情况责任链就是把每个分支抽出来一个文件,代码逻辑会清晰很多,而且先后处理顺序也是可以自定义的。

优缺点

  • 优点:
    1. 降低耦合度。它将请求的发送者和接收者解耦。
    2. 简化了对象。使得对象不需要知道链的结构。
    3. 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
    4. 增加新的请求处理类很方便。
  • 缺点:
    1. 不能保证请求一定被接收(可能所有对象都无法处理此请求)
    2. 写代码时不小心会造成循环调用
    3. 调试不方便,因为多个对象来回跳,没有 if 简单明了

类图

简单实现

场景

现在要实现一个日志记录器,根据传入的参数来判断是打印什么类型的日志

预先准备

传入的参数类型是日志类型的枚举:

1
2
3
4
5
6
7
8
public enum LoggerType {
/** 普通日志 */
INFO,
/** 调试日志 */
DEBUG,
/** 错误日志 */
ERROR
}

不用设计模式的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class OriginalDemo {

private void logMessage(LoggerType loggerType, String message) {
if (loggerType == LoggerType.INFO) {
System.out.println("INFO -- " + message);
} else if (loggerType == LoggerType.DEBUG) {
System.out.println("DEBUG -- " + message);
} else if (loggerType == LoggerType.ERROR) {
System.out.println("ERROR -- " + message);
} else {
System.out.println("无法识别日志类型:" + loggerType);
}
}

public static void main(String[] args) {
OriginalDemo originalDemo = new OriginalDemo();
originalDemo.logMessage(LoggerType.INFO, "INFO 日志");
originalDemo.logMessage(LoggerType.DEBUG, "DEBUG 日志");
originalDemo.logMessage(LoggerType.ERROR, "ERROR 日志");
}

}

运行结果:

1
2
3
INFO -- INFO 日志
DEBUG -- DEBUG 日志
ERROR -- ERROR 日志

使用责任链模式

整体思路梳理:就是把日志抽象出一个日志打印抽象类,然后三个分支依次继承,里面写自己的日志打印逻辑;日志抽象类中规划了 setNextLogger 的方法,设置下家,然后定义了 logMessage 方法,来供外部调用,然后 logMessage 中就是定义了判断日志类型的逻辑;

日志链顶层抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class AbstractLogger {

protected LoggerType loggerType;

protected AbstractLogger nextLogger;

public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}

public void logMessage(LoggerType loggerType, String message) {
if (loggerType == this.loggerType) {
outputMessage(message);
}
if (nextLogger != null) {
nextLogger.logMessage(loggerType, message);
}
}

/**
* 实际输出日志逻辑
* @param message 日志信息
*/
abstract protected void outputMessage(String message);
}

三种类型的日志打印器

1
2
3
4
5
6
7
8
9
10
11
public class DebugLogger extends AbstractLogger {

public DebugLogger() {
this.loggerType = LoggerType.DEBUG;
}

@Override
protected void outputMessage(String message) {
System.out.println("DEBUG -- " + message);
}
}
1
2
3
4
5
6
7
8
9
10
11
public class ErrorLogger extends AbstractLogger {

public ErrorLogger() {
this.loggerType = LoggerType.ERROR;
}

@Override
protected void outputMessage(String message) {
System.out.println("ERROR -- " + message);
}
}
1
2
3
4
5
6
7
8
9
10
11
public class InfoLogger extends AbstractLogger {

public InfoLogger() {
this.loggerType = LoggerType.INFO;
}

@Override
protected void outputMessage(String message) {
System.out.println("INFO -- " + message);
}
}

测试使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ChainPatternDemo {

private static AbstractLogger getLoggerChain() {
InfoLogger infoLogger = new InfoLogger();
DebugLogger debugLogger = new DebugLogger();
ErrorLogger errorLogger = new ErrorLogger();
// info 下家是 debug 接手
infoLogger.setNextLogger(debugLogger);
// debug 下家 error 接手
debugLogger.setNextLogger(errorLogger);
// 责任链危险的地方,一个写不好就会循环调用,添加以下代码就会循环调用
// errorLogger.setNextLogger(infoLogger);
return infoLogger;
}


public static void main(String[] args) {
AbstractLogger loggerChain = getLoggerChain();

loggerChain.logMessage(LoggerType.INFO, "INFO 日志测试");
loggerChain.logMessage(LoggerType.DEBUG, "DEBUG 日志测试");
loggerChain.logMessage(LoggerType.ERROR, "ERROR 日志测试");
}
}

输出结果:

1
2
3
INFO -- INFO 日志测试
DEBUG -- DEBUG 日志测试
ERROR -- ERROR 日志测试