Chain of Responsibility - 责任链设计模式

8 阅读2分钟

什么是责任链模式?

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

优缺点

优点:

1.降低耦合度。它将请求的发送者和接收者解耦。
2.单一职责原则,降低各环节处理的复杂度。
3.开闭原则,通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

缺点:

1.不能保证请求一定被接收和处理。
2.系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3.要注意链内成员的顺序,顺序错了会导致意想不到的错误。

示例

责任链模式在 Java 中有着广泛的应用,例如Spring web中的拦截器,filter等,日志分等级记录。再例如很多工作中的审批流等。我们以日志处理场景为例,日志输出的阈值设置非常重要,一般情况下,在生产环境阈值会被设为 ERROR 或 WARN 来过滤出重要的日志信息,而在其他环境一般会设为 INFO。其逻辑为

Log LevelOutput
ERROR只有 Error 等级日志会输出
WARNWarn 和 Error 等级的日志会输出
INFOError, Warn, Error 全部输出
enum LogLevel {
    INFO,
    WARN,
    ERROR
}

interface ILogHandler {
    void log(LogLevel level, String msg);
}

class Logger implements ILogHandler {
    private Logger nextLogger;
    public Logger(Logger nextLogger) {
        this.nextLogger = nextLogger;
    }
    @Override
    public void log(LogLevel level, String msg) {
        if (nextLogger != null) {
            nextLogger.log(level, msg);
        }
    }
}

class ErrorLogHandler extends Logger {
    public ErrorLogHandler(Logger nextLogger) {
        super(nextLogger);
    }
    @Override
    public void log(LogLevel level, String msg) {
        if (LogLevel.ERROR == level) {
            System.out.println(msg);
            return;
        } else {
            super.log(level, msg);
        }
    }
}

class WarnLogHandler extends Logger {
    public WarnLogHandler(Logger nextLogger) {
        super(nextLogger);
    }
    @Override
    public void log(LogLevel level, String msg) {
        if (LogLevel.WARN == level) {
            System.out.println(msg);
            return;
        } else {
            super.log(level, msg);
        }
    }
}

class InfoLogHandler extends Logger {
    public InfoLogHandler(Logger nextLogger) {
        super(nextLogger);
    }
    @Override
    public void log(LogLevel level, String msg) {
        if (LogLevel.INFO == level) {
            System.out.println(msg);
            return;
        } else {
            super.log(level, msg);
        }
    }
}


class LoggerFactory {
    public static Logger getLogger(LogLevel logThreshold) {
        switch (logThreshold) {
            case ERROR: return new ErrorLogHandler(null);
            case WARN: return new WarnLogHandler(new ErrorLogHandler(null));
            default: return new InfoLogHandler(new WarnLogHandler(new ErrorLogHandler(null)));
        }
    }
}

public static void main(String[] args) {
    System.out.println("-----Info level threshold-----");
    final Logger infoLogger = LoggerFactory.getLogger(LogLevel.INFO);
    infoLogger.log(LogLevel.ERROR, "[ERROR] Out of memory!");
    infoLogger.log(LogLevel.WARN, "[WARN] Invalid company name");
    infoLogger.log(LogLevel.INFO, "[INFO] Hello World!");

    System.out.println("-----Warn level threshold-----");
    final Logger warnLogger = LoggerFactory.getLogger(LogLevel.WARN);
    warnLogger.log(LogLevel.ERROR, "[ERROR] Out of memory!");
    warnLogger.log(LogLevel.WARN, "[WARN] Invalid company name");
    warnLogger.log(LogLevel.INFO, "[INFO] Hello World!");

    System.out.println("-----Error level threshold-----");
    final Logger errorLogger = LoggerFactory.getLogger(LogLevel.ERROR);
    errorLogger.log(LogLevel.ERROR, "[ERROR] Out of memory!");
    errorLogger.log(LogLevel.WARN, "[WARN] Invalid company name");
    errorLogger.log(LogLevel.INFO, "[INFO] Hello World!");
}

Output

-----Info level threshold-----
[ERROR] Out of memory!
[WARN] Invalid company name
[INFO] Hello World!
-----Warn level threshold-----
[ERROR] Out of memory!
[WARN] Invalid company name
-----Error level threshold-----
[ERROR] Out of memory!