1.定义
Chain是⼀种⾏为设计模式, 允许你将请求沿着处理者链进⾏发送。 收到请求后, 每个处理者均可对请求进⾏处理, 或将其传递给链上的下个处理者。
2.案例分析
假如你正在开发⼀个在线订购系统。 你希望对系统访问进⾏限制, 只 允许认证⽤户创建订单。 此外, 拥有管理权限的⽤户也拥有所有订单 的完全访问权限。
简单规划后, 你会意识到这些检查必须依次进⾏。 只要接收到包含⽤户凭据的请求, 应⽤程序就可尝试对进⼊系统的⽤户进⾏认证。 但如果由于⽤户凭据不正确⽽导致认证失败, 那就没有必要进⾏后续检查了。
在接下来的⼏个⽉⾥, 你实现了后续的⼏个检查步骤。
⼀位同事认为直接将原始数据传递给订购系统存在安全隐患。 因此你新增了额外的验证步骤来清理请求中的数据。
过了⼀段时间, 有⼈注意到系统⽆法抵御暴⼒密码破解⽅式的攻击。为了防范这种情况, 你⽴刻添加了⼀个检查步骤来过滤来⾃同⼀ IP 地址的重复错误请求。
⼜有⼈提议你可以对包含同样数据的重复请求返回缓存中的结果, 从⽽提⾼系统响应速度。 因此, 你新增了⼀个检查步骤, 确保只有没有满⾜条件的缓存结果时请求才能通过并被发送给系统。
检查代码本来就已经混乱不堪, ⽽每次新增功能都会使其更加臃肿。修改某个检查步骤有时会影响其他的检查步骤。 最糟糕的是, 当你希望复⽤这些检查步骤来保护其他系统组件时, 你只能复制部分代码,因为这些组件只需部分⽽⾮全部的检查步骤。
系统会变得让⼈⾮常费解, ⽽且其维护成本也会激增。 你在艰难地和这些代码共处⼀段时间后, 有⼀天终于决定对整个系统进⾏重构。
3.解决方案
与许多其他⾏为设计模式⼀样, 责任链会将特定⾏为转换为被称作处理者的独⽴对象。 在上述示例中, 每个检查步骤都可被抽取为仅有单个⽅法的类, 并执⾏检查操作。 请求及其数据则会被作为参数传递给该⽅法。
模式建议你将这些处理者连成⼀条链。 链上的每个处理者都有⼀个成员变量来保存对于下⼀处理者的引⽤。 除了处理请求外, 处理者还负责沿着链传递请求。 请求会在链上移动, 直⾄所有处理者都有机会对其进⾏处理。
最重要的是: 处理者可以决定不再沿着链传递请求, 这可⾼效地取消所有后续处理步骤。
在我们的订购系统示例中, 处理者会在进⾏请求处理⼯作后决定是否继续沿着链传递请求。 如果请求中包含正确的数据, 所有处理者都将执⾏⾃⼰的主要⾏为, ⽆论该⾏为是身份验证还是数据缓存。
4.
1、处理者(Handler)声明了所有具体处理者的通用接口。
2、基础处理者(Base Handler)是一个可选的类,你可以将所有处理者共用的样本放置在其中。
3、具体处理者(Concrete Handler)包含处理请求的实际代码。每个处理者接收到实际请求后,都必须决定是否进行处理,以及是否沿着链传递请求。
4、客户端(Client)可根据程序逻辑一次性或者动态地生成链。值得注意的是,请求可发送给链上的任意一个处理者,而非必须是第一个处理者。
5.代码示例
顶层接口:
public interface Handler { // 定义下一个处理器是什么,从外侧进行传入 public void setNext(Handler next); // 定义当前的处理器要做什么事情 public void handle(Map<String,Object> request); }
抽象处理器:
public abstract class AbstractHandler implements Handler { // 内部持有下一个Handler对象 private Handler next; // 从外部传入Handler 进行链表的构建形成,完成上级和下级Handler之间的关联 @Override public void setNext(Handler next) { this.next = next; } // 获取下一个执行的Handler public Handler getNext() { return next; } }
认证处理器:
public class AuthHandler extends AbstractHandler{ @Override public void handle(Map<String,Object> request) { // 执行业务逻辑认证操作 if(request.get("username").equals("admin") && request.get("password").equals("admin")) { System.out.println("⽤户信息认证通过"); // 用户认证通过调用下一个处理器的handle方法进行执行 if(this.getNext() != null){ this.getNext().handle(request); }else{ throw new RuntimeException("请求没有被送达ProceedHandler"); } // 用户认证未通过,中断责任链执行 }else{ throw new RuntimeException("⽤户名或密码错误"); } } }
缓存处理器:
public class CacheHandler extends AbstractHandler{ @Override public void handle(Map<String,Object> request) { System.out.println("已将当前请求转存⾄Redis缓存"); // 如果当前处理后还有其他处理器,则执行后续处理器 if(this.getNext() != null){ this.getNext().handle(request); }else{ throw new RuntimeException("请求没有被送达ProceedHandler"); } } }
指标收集处理器:
public class MetricsHandler extends AbstractHandler{ @Override public void handle(Map<String,Object> request) { System.out.println("已获取当前运⾏指标并发送⾄Prometheus"); // 如果当前处理后还有其他处理器,则执行后续处理器 if(this.getNext() != null){ this.getNext().handle(request); }else{ throw new RuntimeException("请求没有被送达ProceedHandler"); } } }
业务转发处理器,责任链处理的最终节点
public class ProceedHandler extends AbstractHandler{ @Override public void handle(Map<String,Object> request) { System.out.println("请求已被转发给业务系统,进行后续业务处理"); } }
客户端
public class Client { public static void main(String[] args) { // 实例化调用链上的处理器 Handler authHandler = new AuthHandler(); Handler cacheHandler = new CacheHandler(); Handler metricsHandler = new MetricsHandler(); Handler proceedHandler = new ProceedHandler(); // 进行责任链的构建 authHandler.setNext(cacheHandler); cacheHandler.setNext(metricsHandler); metricsHandler.setNext(proceedHandler); // 模拟请求数据 Map request = new HashMap<>(); request.put("username", "admin"); request.put("password", "admin"); // 进行责任链的执行 authHandler.handle(request); } }
6.责任链模式适合应⽤场景
1、当程序需要使⽤不同⽅式处理不同种类请求, ⽽且请求类型和顺序预先未知时, 可以使⽤责任链模式。该模式能将多个处理者连接成⼀条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进⾏处理。 这样所有处理者都有机会来处理请求。
2、当必须按顺序执⾏多个处理者时, 可以使⽤该模式。⽆论你以何种顺序将处理者连接成⼀条链, 所有请求都会严格按照顺序通过链上的处理者。
3、如果所需处理者及其顺序必须在运⾏时进⾏改变, 可以使⽤责任链模式。如果在处理者类中有对引⽤成员变量的设定⽅法, 你将能动态地插⼊和移除处理者, 或者改变其顺序。
7.责任链模式优点
你可以控制请求处理的顺序。
单⼀职责原则。 你可对发起操作和执⾏操作的类进⾏解耦。
开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。
8.责任链模式缺点
不能保证每个请求⼀定被处理。由于⼀个请求没有明确的接收者,所以不能保证它⼀定会被处理,该请求可能⼀直传到链的末端都得不到处理。
对⽐较⻓的职责链,请求的处理可能涉及多个处理对象,系统性能将受到⼀定影响。
职责链建⽴的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置⽽导致系统出错,如可能会造成循环调⽤。
转载请注明:西门飞冰的博客 » 设计模式—责任链模式