责任链模式

# 责任链模式 > 责任链模式(Chain of Responsibility Patter)是将链中的每一个节点看成是一个对象,每个节点处理的请求均不相同, >并且内部自动维护下一个节点对象。当一个请求从链的头部发出时,会沿着链的路径一次传递给每一个节点对象,直到有节点处理这个请求为止; > 责任链模式属于行为型模式 ## 类图 ![](https://oscimg.oschina.net/oscnet/up-346d95211f8ced1530fe1098a480a35a535.png) ### Handler 责任链抽象类 链式结构 ```text public abstract class Handler { protected Handler nextHandler; public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } public abstract void handleRequest(String request); } ``` ### ConcreteHandlerA 节点对象A ```text public class ConcreteHandlerA extends Handler { @Override public void handleRequest(String request) { if ("reqA".equals(request)) { System.err.println(this.getClass().getSimpleName() + "deal this request " + request); return; } if (this.nextHandler != null) { this.nextHandler.handleRequest(request); } } } ``` ### ConcreteHandlerB 节点对象B ```text public class ConcreteHandlerB extends Handler { @Override public void handleRequest(String request) { if ("reqB".equals(request)) { System.err.println(this.getClass().getSimpleName() + "deal this request " + request); return; } if (this.nextHandler != null) { this.nextHandler.handleRequest(request); } } } ``` ### DemoTest 测试类 ```text public class DemoTest { public static void main(String[] args) { Handler handlerA = new ConcreteHandlerA(); Handler handlerB = new ConcreteHandlerB(); handlerA.setNextHandler(handlerB); handlerA.handleRequest("reqB"); } } // 输出结果: //ConcreteHandlerBdeal this request reqB ``` ## 业务 下面我们举一个业务场景的例子🌰来展示一下什么是责任链模式: 我们登录时,肯定要校验用户名和密码有没有传参,如果参数都是空的话,那也没有必要执行后面的登录部分,直接返回了;如果参数是合法的,那我们就要校验 数据库是否存在这个用户了,如果不存在该用户,那就直接返回;如果存在,那就要校验用户有没有权限访问正在请求的资源,如果有权限,则可以 正常访问资源,如果没有权限,则抛出异常; ### 非责任链模式写法 ```text // 以下是伪代码 public void login(String userName,String pwd){ if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(pwd)){ throw new IllegalArgumentException(); } Member member = selectUser(userName,pwd); if (Objects.isNull(member)){ throw new IllegalArgumentException(); } if (!validateRoot(member)){ throw new IllegalArgumentException(); } System.err.println("Login success!"); } ``` 这种写法虽然简单,但是将所有的操作全部杂糅在一起了,结构是十分混乱的; 下面👇看一下如果使用责任链模式该如何实现: ![](https://oscimg.oschina.net/oscnet/up-f94e4e83f493d7a998e1e8bbb36f3c8eb4b.png) ### Handler 抽象责任链处理器 ```text public abstract class Handler { protected Handler next; public void next(Handler next){ this.next = next; } public abstract void doHandler(Member member); } ``` ### ValidateHandler 参数校验处理器 ```text public class ValidateHandler extends Handler{ @Override public void doHandler(Member member) { if (StringUtils.isEmpty(member.getLoginName()) || StringUtils.isEmpty(member.getLoginPass())){ System.err.println("登录名或者密码为空!"); return; } System.err.println("参数校验成功!"); next.doHandler(member); } } ``` ### 登录处理器 ```text public class LoginHandler extends Handler { @Override public void doHandler(Member member) { System.err.println("login success!"); member.setRoleName("root"); next.doHandler(member); } } ``` ### 鉴权处理器 ```text public class AuthHandler extends Handler{ @Override public void doHandler(Member member) { if ("root".equals(member.getRoleName())){ System.err.println("Auth success!"); return; } System.err.println("Auth failed!"); } } ``` ### Member成员类 ```text public class Member { private String loginName; private String loginPass; private String roleName; public Member(String loginName, String loginPass) { this.loginName = loginName; this.loginPass = loginPass; } // GETTER SETTER .... } ``` ### DemoTest 测试类 ```text public class DemoTest { // 首先创建所有的处理器,设置后它们的先后顺序,设置next节点,完成之后在执行链路操作; public static void main(String[] args) { ValidateHandler validateHandler = new ValidateHandler(); LoginHandler loginHandler = new LoginHandler(); validateHandler.next(loginHandler); AuthHandler authHandler = new AuthHandler(); loginHandler.next(authHandler); Member member = new Member("xiaoming", "222"); validateHandler.doHandler(member); } } ``` 上面的写法,看起来有没有比非责任链写法要B格高一些,但是,这样每次创建节点对象,好像又显得不太爽,有些臃肿的意思! 而且调整节点之间的顺序时也比较复杂,容易改错! 那我们就进一步优化以下吧! ## 责任链模式+建造者模式 优化 ### 添加建造器builder ```text public abstract class Handler<T> { protected Handler next; public void next(Handler next) { this.next = next; } public abstract void doHandler(Member member); // 暴露建造器,将节点对象以链表的形式串起来 public static class Builder<T> { private Handler<T> head; private Handler<T> tail; public Handler<T> build() { return this.head; } public Builder<T> addHandler(Handler<T> handler) { if (this.head == null) { this.head = this.tail = handler; return this; } this.tail.next(handler); this.tail = handler; return this; } } } ``` ### 测试 ```text public static void main(String[] args) { Member member = new Member("xiaoming", null); Handler.Builder<Handler> builder = new Handler.Builder(); builder.addHandler(new ValidateHandler()) .addHandler(new LoginHandler()) .addHandler(new AuthHandler()) .build() .doHandler(member); } ``` 通过添加建造器之后,责任链的组装与调用是不是显得很清晰,每个节点对象各司其职,如果需要本节点执行,则执行,如果不是,则交给下一个节点继续! ## 责任链适用的场景 - **多个对象处理同一个请求,但具体由那个对象处理则在运动时动态决定** - **在不明确🈯指定接受者的情况下,向多个对象的一个提交请求** - **可以动态指定一组对象处理请求** > 举一些实际的例子: javax.servlet.Filter的doFilter方法就是使用的责任链模式; ### org.springframework.web.filter.CompositeFilter#doFilter ```text public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { (new CompositeFilter.VirtualFilterChain(chain, this.filters)).doFilter(request, response); } ``` 这是Spring框架中的一个实现(javax.servlet.Filter),下面展开具体的代码: ### org.springframework.web.filter.CompositeFilter ```text public class CompositeFilter implements Filter { //Filter 这里的Filter相当于处理节点,存放在List中,和我们上面的Handler存放在建造者的链式结构中,异曲同工。 private List<? extends Filter> filters = new ArrayList(); public CompositeFilter() { } public void setFilters(List<? extends Filter> filters) { this.filters = new ArrayList(filters); } public void init(FilterConfig config) throws ServletException { Iterator var2 = this.filters.iterator(); while(var2.hasNext()) { Filter filter = (Filter)var2.next(); filter.init(config); } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { (new CompositeFilter.VirtualFilterChain(chain, this.filters)).doFilter(request, response); } public void destroy() { int i = this.filters.size(); while(i-- > 0) { Filter filter = (Filter)this.filters.get(i); filter.destroy(); } } private static class VirtualFilterChain implements FilterChain { private final FilterChain originalChain; private final List<? extends Filter> additionalFilters; private int currentPosition = 0; public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) { this.originalChain = chain; this.additionalFilters = additionalFilters; } public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (this.currentPosition == this.additionalFilters.size()) { this.originalChain.doFilter(request, response); // 通过currentPosition的移动,转移到链路上的不同处理节点,这就是责任链模式的体现 } else { ++this.currentPosition; Filter nextFilter = (Filter)this.additionalFilters.get(this.currentPosition - 1); nextFilter.doFilter(request, response, this); } } } } ``` 像上面的例子还有很多很多,比如Spring Security和Shiro中的拦截器就是很典型的责任链模式,还有Netty中的 io.netty.channel.ChannelPipeline等等, 都是责任链模式; ## 责任链模式的优点👍👍👍 - **将请求与处理解耦** - **请求处理者(节点对象)只需要关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一个节点对象** - **具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待处理请求结果** - **链路结构灵活,可以通过改变链路结果动态的新增和删除责任** - **易于扩展新的请求节点(符合开闭原则)** ## 责任链模式的缺点 - **责任链太长或者处理时间太长,导致系统性能下降** - **如果节点对象存在循环♻️引用时,会造成死循环,导致系统崩溃,所以这个在设计的时候一定要注意不能形成闭环⚠️⚠️⚠️**