Appearance
允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
流程梳理
- 客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象
- 让多个对象都有机会处理请求,避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条调用链,并沿着这条链传递该请求,直到有一个对象处理它才终止
- 有两个核心行为:一是处理请求,二是将请求传递到下一节点
应用刨析
假如你正在开发一个在线订购系统。 你希望对系统访问进行限制, 只允许认证用户创建订单。 此外, 拥有管理权限的用户也拥有所有订单的完全访问权限。简单规划后, 你会意识到这些检查必须依次进行。 只要接收到包含用户凭据的请求, 应用程序就可尝试对进入系统的用户进行认证。 但如果由于用户凭据不正确而导致认证失败, 那就没有必要进行后续检查了。
请求必须经过一系列检查后才能由订购系统来处理。在接下来的几个月里, 你实现了后续的几个检查步骤。
- 一位同事认为直接将原始数据传递给订购系统存在安全隐患。 因此你新增了额外的验证步骤来清理请求中的数据。
- 过了一段时间, 有人注意到系统无法抵御暴力密码破解方式的攻击。 为了防范这种情况, 你立刻添加了一个检查步骤来过滤来自同一 IP 地址的重复错误请求。
- 又有人提议你可以对包含同样数据的重复请求返回缓存中的结果, 从而提高系统响应速度。 因此, 你新增了一个检查步骤, 确保只有没有满足条件的缓存结果时请求才能通过并被发送给系统。
**代码变得越来越多, 也越来越混乱。**检查代码本来就已经混乱不堪, 而每次新增功能都会使其更加臃肿。 修改某个检查步骤有时会影响其他的检查步骤。 最糟糕的是, 当你希望复用这些检查步骤来保护其他系统组件时, 你只能复制部分代码, 因为这些组件只需部分而非全部的检查步骤。
系统会变得让人非常费解, 而且其维护成本也会激增。 你在艰难地和这些代码共处一段时间后, 有一天终于决定对整个系统进行重构。
应用场景
- Apache Tomcat 对 Encoding 编码处理的处理,SpringBoot 里面的拦截器、过滤器链
- 在请求处理者不明确的情况下向多个对象中的一个提交请求
- 如果有多个对象可以处理同一个请求,但是具体由哪个对象处理是由运行时刻动态决定的,这种对象就可以使用职责链模式
需求分析
风控规则,就是对于每个场景,定义一些规则,来进行相应的控制,比如银行借款、支付宝提现、大额转账等 会触发不同的策略。像互联网金融行业,除了公司内部政策,所处的外部环境经常发生变化,比如国家经常会出政策,这些都经常需要调整相应的风控参数和风控级别。
例子:支付宝转账,根据转账额度不同,会触发的风控级别不一样,1000元以下直接转,1千到1万需要手机号验证码,1万到以上需要刷脸验证。
编码实现
创建一个请求类
java
@Data
public class Request {
/**
* 类别
*/
private String requestType;
/**
* 金额
*/
private int money;
}
创建一个枚举用于记录状态
java
public enum RequestType {
/**
* 转账
*/
TRANSFER,
/**
* 提现
*/
CASH_OUT
}
创建一个风控管理类
java
public abstract class RiskControlManager {
protected String name;
public RiskControlManager(String name) {
this.name = name;
}
/**
* 更严格的风控策略
*/
protected RiskControlManager superior;
/**
* 设置更严格的风控策略
*/
public void setSuperior(RiskControlManager superior){
this.superior = superior;
}
/**
* 处理请求
* @param request
*/
public abstract void handlerRequest(Request request);
}
创建具体风控实现类,初级风控
java
public class FirstRiskControl extends RiskControlManager{
public FirstRiskControl(String name) {
super(name);
}
@Override
public void handlerRequest(Request request) {
if (RequestType.valueOf(request.getRequestType())!=null && request.getMoney()<=1000){
System.out.println("普通操作输入支付密码即可");
System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney());
}else {
// 下一个节点进行处理
if (superior!=null){
superior.handlerRequest(request);
}
}
}
}
中级风控
java
public class SecondRiskControl extends RiskControlManager{
public SecondRiskControl(String name) {
super(name);
}
@Override
public void handlerRequest(Request request) {
if (RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>1000 && request.getMoney()<10000){
System.out.println("中等风控,输入支付密码+短信验证码");
System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney());
}else {
// 下一个节点进行处理
if (superior!=null){
superior.handlerRequest(request);
}
}
}
}
高级风控
java
public class ThirdRiskControl extends RiskControlManager{
public ThirdRiskControl(String name) {
super(name);
}
@Override
public void handlerRequest(Request request) {
if (RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>=10000){
System.out.println("中等风控,输入支付密码+短信验证码+人脸识别");
System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney());
}else {
// 下一个节点进行处理
if (superior!=null){
superior.handlerRequest(request);
}
}
}
}
使用
java
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入金额");
int money = sc.nextInt();
RiskControlManager firstControlManager = new FirstRiskControl("初级风控");
RiskControlManager secondControlManager = new SecondRiskControl("中级风控");
RiskControlManager thirdControlManager = new ThirdRiskControl("高级风控");
// 形成调用链
firstControlManager.setSuperior(secondControlManager);
secondControlManager.setSuperior(thirdControlManager);
Request request1 = new Request();
request1.setRequestType(RequestType.CASH_OUT.name());
request1.setMoney(money);
firstControlManager.handlerRequest(request1);
}
小结
优点
- 客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者 降低了耦合度
- 通过改变链内的调动它们的次序,允许动态地新增或者删除处理类,比较很方便维护
- 增强了系统的可扩展性,可以根据需要增加新的请求处理类,满足开闭原则
- 每个类只需要处理自己该处理的工作,明确各类的责任范围,满足单一职责原则
缺点
- 处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象
- 不能保证请求一定被接收;
- 如果链路比较长,系统性能将受到一定影响,而且在进行代码调试时不太方便