责任链模式是将链中的每一个节点看作是一个对象,每个节点处理的请求都不相同,且内部自动维护下一个节点对象。每个请求从链式首端发出,沿着链依次经过每个节点,如果当前节点无法处理自动传递给下一个节点,直到链上某个节点处理完请求,则链结束。属于行为型模式。
适用场景:
- 多个对象可以处理同一请求,但具体哪个对象处理在运行时动态决定;
- 在不明确接收者情况下,向多个对象提交一个请求;
- 可动态指定一组对象处理请求;
责任链在框架中的应用:
JDK中Filter, Spring对FilterChain进行了实现,通过List将所有Filter封装起来
Netty中ChannelPipeline, pipeline由ChannelHandler组成,pipeline中有head和tail组成一个双向链,ChannelHandler分为InBoundHandler(数据输入进行解码)和OutBoundHandler(对数据进行编码输出)。
优点:
- 将请求与处理解耦;
- 请求处理者(链上的节点对象)只需要关注自己相关的请求进行处理,不相关的请求直接转给下一个节点对象;
- 可以链式传递处理请求,请求发送者无须知道链路结构和细节,只需等待处理结果;
- 链路结构灵活,可以通过改变链路结构动态新增或删除处理过程;
- 易于扩展新的请求处理类,符合开闭原则;
缺点:
- 如果责任链太长或处理时间过长,会影响整体性能;
- 如果节点对象存在循环引用,有可能出现死循环,导致系统崩溃;
代码演示
下面写个简单的模拟用户登录的代码来演示下责任链模式的使用
使用责任链模式前代码
public class LoginService {
public void login(String username, String pwd) {
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(pwd)) {
System.out.println("用户名或密码为空!");
return;
}
User user = checkExist(username, pwd);
if (null == user) {
System.out.println("用户不存在!");
return;
}
System.out.println("登陆成功");
if (!"admin".equals(user.getRole())) {
System.out.println("您不是管理员,没有操作权限!");
return;
}
System.out.println("操作成功");
}
private User checkExist(String username, String pwd) {
Map<String, User> userMap = new HashMap<>();
userMap.put("Lucifer&&&admin", new User("Lucifer", "admin", "admin"));
if (userMap.containsKey(username + "&&&" + pwd)) {
return userMap.get(username + "&&&" + pwd);
}
return null;
}
}
public class LoginTest {
public static void main(String[] args) {
LoginService service = new LoginService();
service.login("Lucifer", "admin");
}
}
使用责任链后代码
链上节点抽象类
public abstract class AbstractHandler {
protected AbstractHandler nextHandler;
protected void next(AbstractHandler handler) {
this.nextHandler = handler;
}
public abstract void doHandler(User obj);
public static class Builder {
private AbstractHandler head;
private AbstractHandler tail;
public Builder addHandler(AbstractHandler handler) {
if (this.head == null) {
this.head = this.tail = handler;
return this;
}
this.tail.next(handler);
this.tail = handler;
return this;
}
public AbstractHandler build() {
return this.head;
};
}
}
链上的三个节点处理类
public class ValidationHandler extends AbstractHandler {
@Override
public void doHandler(User user) {
if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPwd())) {
System.out.println("用户名或密码为空!");
return;
}
this.nextHandler.doHandler(user);
}
}
public class DBHandler extends AbstractHandler {
@Override
public void doHandler(User u) {
User user = checkExist(u.getUsername(), u.getPwd());
if (null == user) {
System.out.println("用户不存在!");
return;
}
System.out.println("登陆成功");
this.nextHandler.doHandler(user);
}
private User checkExist(String username, String pwd) {
Map<String, User> userMap = new HashMap<>();
userMap.put("Lucifer&&&admin", new User("Lucifer", "admin", "admin"));
if (userMap.containsKey(username + "&&&" + pwd)) {
return userMap.get(username + "&&&" + pwd);
}
return null;
}
}
public class AuthHandler extends AbstractHandler {
@Override
public void doHandler(User user) {
if (!"admin".equals(user.getRole())) {
System.out.println("您不是管理员,没有操作权限!");
return;
}
System.out.println("操作成功");
}
}
将所有节点的处理类串成一个链,并从链首执行
public class LoginService {
public void login(String username, String pwd) {
AbstractHandler.Builder builder = new AbstractHandler.Builder();
builder.addHandler(new ValidationHandler()).addHandler(new DBHandler()).addHandler(new AuthHandler());
User u = new User();
u.setUsername(username);
u.setPwd(pwd);
builder.build().doHandler(u);
}
}
public class LoginTest {
public static void main(String[] args) {
LoginService service = new LoginService();
service.login("Lucifer", "admin");
}
}
面试题
责任链模式的实现原理?
责任链模式有两种实现方式,单向链表,双向链表,通过上下文保存头和尾,每个handler中设置下一个节点,但是链不能太长,否则影响性能,还要避免节点之间互相引用出现死循环