责任链模式
责任链模式(Chain of Responsibility Patter)是将链中的每一个节点看成是一个对象,每个节点处理的请求均不相同,
并且内部自动维护下一个节点对象。当一个请求从链的头部发出时,会沿着链的路径一次传递给每一个节点对象,直到有节点处理这个请求为止;责任链模式属于行为型模式
类图
Handler 责任链抽象类 链式结构
1 2 3 4 5 6 7 8 9 10
| public abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; }
public abstract void handleRequest(String request); }
|
ConcreteHandlerA 节点对象A
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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
1 2 3 4 5 6 7 8 9 10 11 12
| 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 测试类
1 2 3 4 5 6 7 8 9 10
| public class DemoTest { public static void main(String[] args) { Handler handlerA = new ConcreteHandlerA(); Handler handlerB = new ConcreteHandlerB(); handlerA.setNextHandler(handlerB); handlerA.handleRequest("reqB"); } }
|
业务
下面我们举一个业务场景的例子🌰来展示一下什么是责任链模式:
我们登录时,肯定要校验用户名和密码有没有传参,如果参数都是空的话,那也没有必要执行后面的登录部分,直接返回了;如果参数是合法的,那我们就要校验数据库是否存在这个用户了,如果不存在该用户,那就直接返回;如果存在,那就要校验用户有没有权限访问正在请求的资源,如果有权限,则可以正常访问资源,如果没有权限,则抛出异常;
非责任链模式写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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!"); }
|
这种写法虽然简单,但是将所有的操作全部杂糅在一起了,结构是十分混乱的;
下面👇看一下如果使用责任链模式该如何实现:
Handler 抽象责任链处理器
1 2 3 4 5 6 7 8 9 10
| public abstract class Handler {
protected Handler next;
public void next(Handler next){ this.next = next; }
public abstract void doHandler(Member member); }
|
ValidateHandler 参数校验处理器
1 2 3 4 5 6 7 8 9 10 11
| 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); } }
|
登录处理器
1 2 3 4 5 6 7 8
| public class LoginHandler extends Handler { @Override public void doHandler(Member member) { System.err.println("login success!"); member.setRoleName("root"); next.doHandler(member); } }
|
鉴权处理器
1 2 3 4 5 6 7 8 9 10
| 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成员类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Member {
private String loginName; private String loginPass; private String roleName;
public Member(String loginName, String loginPass) { this.loginName = loginName; this.loginPass = loginPass; }
}
|
DemoTest 测试类
1 2 3 4 5 6 7 8 9 10 11 12
| public class DemoTest { 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
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
| 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; } } }
|
测试
1 2 3 4 5 6 7 8 9
| 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
1 2 3
| 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
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
| public class CompositeFilter implements Filter { 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); } 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等等,都是责任链模式;
责任链模式的优点👍👍👍
- 将请求与处理解耦
- 请求处理者(节点对象)只需要关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一个节点对象
- 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待处理请求结果
- 链路结构灵活,可以通过改变链路结果动态的新增和删除责任
- 易于扩展新的请求节点(符合开闭原则)
责任链模式的缺点
- 责任链太长或者处理时间太长,导致系统性能下降
- 如果节点对象存在循环♻️引用时,会造成死循环,导致系统崩溃,所以这个在设计的时候一定要注意不能形成闭环⚠️⚠️⚠️