责任链模式
定义:将链中每一个结点看作一个对象,每个节点处理的请求均不相同,且内部自动维护一个下一结点的对象。每一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个结点对象,直到有对象处理这个请求为止
本质:把请求和处理解耦,可以让请求在处理链中传递,最后被处理
适用场景:
- 多个对象可以处理同一请求,但具体由哪个对象处理则要在运行时动态决定
- 在不明确指定接受者的情况下,向多个对象中的一个提交一次请求
- 可动态指定一组对象处理请求
优点:
- 将请求和处理解耦,请求发送者不需要关心链路结构,只需等待结果
- 节点对象只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的、无法处理的,直接转发到下一个节点对象
- 易于扩展新的或移除旧的节点对象,动态调整链路结构
缺点:
- 责任链太长或者处理时间太长,会影响整体性能
- 如果链路中节点对象存在循环引用的情况,会造成系统崩溃
示例
以登录认证的流程为例,假设登录时需要完成三个操作
- 判断用户名和密码是否为空
- 进行登录
- 权限校验
这些操作都是必须要做的,而且都是有严格的先后顺序,使用 if...else... 代码块不美观,使用责任链模式可以把这些步骤都串联起来
用户类
java
public class Member {
private String loginName;
private String loginPass;
private String roleName;
public Member(String loginName, String loginPass) {
this.loginName = loginName;
this.loginPass = loginPass;
}
// get、set 方法省略
// ...
}处理业务逻辑的抽象 Handler,定义了一个抽象方法 doHandler ,具体处理的逻辑需要子类自行实现,同时维护了下一个处理对象节点。使用建造者模式来完成责任链的构建,更优雅
java
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 Builder<T> addHandler(Handler handler){
if (this.head == null) {
this.head = this.tail = handler;
return this;
}
// 头指针不移动,始终指向第一个,每次都把尾指针往后挪
this.tail.next(handler);
this.tail = handler;
return this;
}
public Handler<T> build(){
return this.head;
}
}
}判断用户名密码是否为空的节点
java
public class ValidateHandler extends Handler {
public void doHandler(Member member) {
if(StringUtils.isEmpty(member.getLoginName()) ||
StringUtils.isEmpty(member.getLoginPass())){
System.out.println("用户名和密码为空");
return;
}
System.out.println("用户名和密码不为空,可以往下执行");
if(null != next) {
next.doHandler(member);
}
}
}完成登录动作的节点
java
public class LoginHandler extends Handler {
public void doHandler(Member member) {
System.out.println("登录成功!");
member.setRoleName("管理员");
if(null != next) {
next.doHandler(member);
}
}
}校验权限的节点
java
public class AuthHandler extends Handler {
public void doHandler(Member member) {
if(!"管理员".equals(member.getRoleName())){
System.out.println("您不是管理员,没有操作权限");
return;
}
System.out.println("允许操作");
}
}使用门面模式封装所有细节,提供一个对外使用的方法
java
public class MemberService {
public void login(String loginName,String loginPass){
Handler.Builder builder = new Handler.Builder();
builder.addHandler(new ValidateHandler())
.addHandler(new LoginHandler())
.addHandler(new AuthHandler());
builder.build().doHandler(new Member(loginName,loginPass));
}
}客户端调用
java
public class Test {
public static void main(String[] args) {
MemberService memberService = new MemberService();
memberService.login("tom","666");
}
}责任链模式在源码中的体现
Spring
Servlet 中定义了一个过滤器的接口,里面有一个抽象方法 doFilter() 负责执行具体的过滤工作,相当于责任链模式中的抽象节点的角色
java
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {}
}其中最后一个参数 FilterChain 也是Servlet 中定义的一个过滤器链的接口
java
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}具体的串联可以看到 Spring 中的 MockFilterChain 类,把过滤器链中的所有 Filter 放到集合中,然后在调用 doFilter() 方法中迭代这个集合,使得集合中的所有 Filter 可以按顺序执行,因为 Filter 中的 doFilter() 方法最后会调用传入的 FilterChain 的 doFilter() 方法
java
public class MockFilterChain implements FilterChain {
@Nullable
private ServletRequest request;
@Nullable
private ServletResponse response;
private final List<Filter> filters;
@Nullable
private Iterator<Filter> iterator;
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
Assert.notNull(request, "Request must not be null");
Assert.notNull(response, "Response must not be null");
Assert.state(this.request == null, "This FilterChain has already been called!");
if (this.iterator == null) {
this.iterator = this.filters.iterator();
}
if (this.iterator.hasNext()) {
Filter nextFilter = (Filter)this.iterator.next();
nextFilter.doFilter(request, response, this);
}
this.request = request;
this.response = response;
}
}