1.介绍
使多个对象都有机会处理请求,从而避免请求的发送者与接受者之间的耦合关系. 将多个接受者连成一条链,沿着该链处理请求,直到请求被处理为止.
类图
角色
抽象处理者: 定义了处理请求的接口或者抽象类,提供了处理请求的的方法和设置下一个处理者的方法。
具体处理者: 实现或者继承抽象这角色,具体的实现处理逻辑.
实例与代码
责任链模式有纯的和不纯的.
纯的:当前接收者要么处理,要么传递给下一个处理者.
不纯的:当前接收者可以处理一部分之后交给下一个处理者.
明显不纯的更加受欢迎嘛,比如常见的企业OA审批流程,一般是请求->直系Leader同意->总监同意
,而不是直系leader直接跳过.
下面将两种分别编码实现以下.
纯的
代码较多,想要更清晰可以移步github.传送门
这里我们用打印log来模拟一下场景:
log有多种级别,debug,info,warn,error.对应着不同的处理方法
debug级别的不做处理.
info打印即可.
warn打印在标准输出之后写入文件保存.
error错误发送邮件给项目owner.
首先实现一个抽象的LogHandler,里面定义了当前handler的级别,下一个处理者.
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 26 27 28 29 30 31 32 33 34
| package design_patterns.chain_of_responsibility_pattern.log;
public abstract class AbstractLogHandler {
public LevelEnum levelEnum;
private AbstractLogHandler nextHandler;
public void setNextHandler( AbstractLogHandler nextHandler) { this.nextHandler = nextHandler; }
public void handlerRequest(LogInfo info) { if (null == info) { return; } if (this.levelEnum.equals(info.levelEnum)) { this.consumeLog(info.content); } else { if (this.nextHandler != null) { this.nextHandler.handlerRequest(info); } else { return; } } }
abstract void consumeLog(String content); }
|
接下来是四个具体的LogHandler
实现.
DEBUG:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package design_patterns.chain_of_responsibility_pattern.log;
public class DebugLogHandler extends AbstractLogHandler {
public DebugLogHandler() { this.levelEnum = LevelEnum.DEBUG; }
@Override void consumeLog(String content) { System.out.println("我是DEBUG,没有做处理"); } }
|
INFO:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package design_patterns.chain_of_responsibility_pattern.log;
public class InfoLogHandler extends AbstractLogHandler {
public InfoLogHandler( ) { this.levelEnum = LevelEnum.INFO; }
@Override void consumeLog(String content) { System.out.println(content); System.out.println("我是INFO,直接打印完事了");
} }
|
WARN:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package design_patterns.chain_of_responsibility_pattern.log;
public class WarnLogHandler extends AbstractLogHandler {
public WarnLogHandler( ) { this.levelEnum = LevelEnum.WARN; }
@Override void consumeLog(String content) { System.out.println(content); System.out.println("我是WARN,不仅打印了还写入文件了."); } }
|
ERROR:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package design_patterns.chain_of_responsibility_pattern.log;
public class ErrorLogHandler extends AbstractLogHandler{
public ErrorLogHandler() { this.levelEnum = LevelEnum.ERROR; }
@Override void consumeLog(String content) { System.out.println(content); System.out.println("我是ERROR,不仅打印了还写入文件了还发了个邮件.");
} }
|
下面是两个Model类:
1 2 3 4 5 6 7 8 9 10 11 12
| package design_patterns.chain_of_responsibility_pattern.log;
public class LogInfo {
public LevelEnum levelEnum;
public String content;
}
|
1 2 3 4 5 6 7 8 9 10 11
| package design_patterns.chain_of_responsibility_pattern.log;
public enum LevelEnum { DEBUG, INFO, WARN, ERROR; }
|
测试代码:
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 26 27 28 29 30 31 32 33 34 35
| package design_patterns.chain_of_responsibility_pattern.log;
public class Test {
public static void main(String [] args ){ LogInfo info = new LogInfo(); info.content = "这是一条WARN测试log"; info.levelEnum = LevelEnum.WARN; LogInfo info1 = new LogInfo(); info1.levelEnum = LevelEnum.ERROR; info1.content = "我是一条严重ERROR的LOG";
AbstractLogHandler debugLog = new DebugLogHandler(); AbstractLogHandler infoLog = new InfoLogHandler(); AbstractLogHandler warnLog = new WarnLogHandler(); AbstractLogHandler errorLog = new ErrorLogHandler();
debugLog.setNextHandler(infoLog); infoLog.setNextHandler(warnLog); warnLog.setNextHandler(errorLog);
debugLog.handlerRequest(info); debugLog.handlerRequest(info1);
}
}
|
输出如下:
1 2 3 4
| 这是一条WARN测试log 我是WARN,不仅打印了还写入文件了. 我是一条严重ERROR的LOG 我是ERROR,不仅打印了还写入文件了还发了个邮件.
|
可以看出,在这样编码之后,在Test类中的代码清晰了许多.(去除掉了大量的if/else,同时,对责任链的初始化也可以移到别的类中,这里不做操作.)
同时,极大的提高了扩展性,假设现在出现了第五种log级别,我们只需要重新编写一个子类,然后再责任链中加入即可.
不纯的
代码较多,想要更清晰可以移步github.传送门
其实这个场景是我学习责任链的初衷,那就是在一个接口内部,我们需要对传入的多个参数(示例中防止代码过多,使用两个参数)进行校验,并返回不同的error_msg
,如果在方法中实现,会有大量的if/else,而且你会发现对参数的校验代码比真正的业务逻辑都多.严重影响了代码的可读性.
下面用简单的代码来实现以下:
首先是三个model类:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| package design_patterns.chain_of_responsibility_pattern.param;
public class BaseResponse {
private int errCode; private String errMsg;
private String content;
public BaseResponse(BaseError error){ this.errCode = error.errCode; this.errMsg = error.errMsg; }
@Override public String toString() { return "errCode:" + errCode + ";" + "errMsg" + errMsg; } }
package design_patterns.chain_of_responsibility_pattern.param;
public enum BaseError { SUCCESS(10000, "成功啦"), NAME_TOO_LONG(10001, "你的名字太长了8,不允许."), NAME_TOO_SHORT(10002, "你的名字不可以这么短"), AGE_TOO_BIG(10003, "千年老妖怪吗?"), AGE_TOO_SMALL(10004, "不支持-0.9以下的年龄哦"), HIGH_TOO_HIGH(10005, "我不管,姚明最高,再高不行"), HIGH_TOO_LOW(10006, "你个lowB,不准用.");
int errCode; String errMsg;
BaseError(int errCode, String errMsg) { this.errCode = errCode; this.errMsg = errMsg; }
}
package design_patterns.chain_of_responsibility_pattern.param;
public class Person {
public String name;
public int age;
public int high;
public Person(String name, int age, int high) { this.name = name; this.age = age; this.high = high; } }
|
接下来是抽象的处理类:
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 26 27 28 29
| package design_patterns.chain_of_responsibility_pattern.param;
public abstract class AbstractParamHandler {
private AbstractParamHandler nextHandler;
public void setNextHandler( AbstractParamHandler nextHandler) { this.nextHandler = nextHandler; }
public BaseResponse handlerRequest(Person person) { BaseResponse response = doCheck(person); if (null == response) { if (this.nextHandler != null) { return this.nextHandler.handlerRequest(person); } else { return new BaseResponse(BaseError.SUCCESS); } } return response; }
public abstract BaseResponse doCheck(Person person); }
|
需要注意的是,这个代码和上面纯的责任链
的区别,在这份代码中,没有进行是否是当前”级别”的判断,而是直接进行处理,如果当前参数校验不通过,直接返回,当前校验通过,继续进行下一次校验,如果全部通过,返回成功.
接下来是名字的处理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package design_patterns.chain_of_responsibility_pattern.param;
public class NameHandler extends AbstractParamHandler {
@Override public BaseResponse doCheck(Person person) { if (person.name == null || person.name.length() < 1) { return new BaseResponse(BaseError.NAME_TOO_SHORT); } else if (person.name.length() > 10) { return new BaseResponse(BaseError.NAME_TOO_LONG); } return null; } }
|
年龄的处理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package design_patterns.chain_of_responsibility_pattern.param;
public class AgeHandler extends AbstractParamHandler {
@Override public BaseResponse doCheck(Person person) { if (person.age < -0.9) { return new BaseResponse(BaseError.AGE_TOO_SMALL); } else if (person.age > 1000) { return new BaseResponse(BaseError.AGE_TOO_BIG); } return null; } }
|
身高的处理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package design_patterns.chain_of_responsibility_pattern.param;
public class HighHandler extends AbstractParamHandler {
@Override public BaseResponse doCheck(Person person) { if (person.high < 40) { return new BaseResponse(BaseError.HIGH_TOO_LOW); } else if (person.high > 236) { return new BaseResponse(BaseError.HIGH_TOO_HIGH); } return null; } }
|
测试代码:
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 26 27 28
| package design_patterns.chain_of_responsibility_pattern.param;
public class Test {
public static void main(String[] args) {
AbstractParamHandler name = new NameHandler(); AbstractParamHandler age = new AgeHandler(); AbstractParamHandler high = new HighHandler(); name.setNextHandler(age); age.setNextHandler(high);
Person person = new Person("huyanshi", 23, 172); System.out.println(name.handlerRequest(person));
Person person1 = new Person("huyanshihuyanshi",22 , 122); System.out.println(name.handlerRequest(person1));
Person person2 = new Person("huyanshi",-10 , 122); System.out.println(name.handlerRequest(person2)); } }
|
对应的输出如下:
1 2 3
| errCode:10000;errMsg成功啦 errCode:10001;errMsg你的名字太长了8,不允许. errCode:10004;errMsg不支持-0.9以下的年龄哦
|
模式总结
优点:
- 请求的发起者不知道请求会被谁处理,解耦.
- 请求的处理者也不知道自己会处在链条的哪个位置,由客户端进行动态的装配.
- 系统的扩展性强,无论是新增处理者还是删除,甚至是打乱顺序重新组合,都十分方便.
缺点:
- 递归调用,可能带来性能问题.
- 递归调用,排查问题不方便.
思考
1. 和门面模式思想的结合
可以发现我们在Test类中的构造责任链
的代码很麻烦,且重复的可能性较高,比如在每个项目中可能LOG的级别只有那么多,却需要每次装配一次.
我们可以再当前代码的基础上,提供一个四种级别LOG责任链
的门面,当没有发生变化时,直接调用门面的构造方法,会自动装配这几种LOG级别并构造成链,这样可以复用代码,且没有破坏扩展性,当新增一个级别之后,我们可以选择建立新的门面,也可以选择修改旧的门面.都比较方便.
2. 和模板方法的区别与联系
如果看过模板方法模式,会发现责任链和模板方法有一点相似.
对应的关系为:
handlerRequest
方法为父类具体方法.
doCheck
为父类抽象方法,每个子类必须自己去实现.
setNextHandler
为钩子方法,父类提供默认实现,子类可以实现可以不实现,当设置或者不设置,会产生控制流程的作用,即为钩子
.
是不是很像呢?
这样结合模板方法模式的好处在哪?首先加了handlerRequest
方法,把请求的传递判断从子类中剥离出来,让子类在doCheck
方法中专心处理请求的业务逻辑,做到了单一职责原则。
果然是大道至简,万法归一啊…
完。
ChangeLog
2019-03-19 完成
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见个人博客——>呼延十