定义
责任链模式将链中每一个节点都看作一个对象,每个节点处理的请求都不同,且内部自动维护下一个节点对象,当一个请求从链式的首段发出时,会沿着责任链预设的路径依次传递到每一个节点对象,直至被链中的某个对象处理为止,属于行为型设计模式。
使用场景:
- 多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定
- 在不明确指定接收者的情况下,向多个对象中的一个提交请求
- 可以动态指定一组对象处理请求
通用写法
public abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler successor){
this.nextHandler = successor;
}
public abstract void handleRequest(String request);
public void handlerNext(String request){
if(this.nextHandler != null){
this.nextHandler.handleRequest(request);
}
}
}
public class HandlerA extends Handler{
@Override
public void handleRequest(String request) {
System.out.println("handlerA:"+request);
handlerNext(request);
}
}
public class HandlerB extends Handler{
@Override
public void handleRequest(String request) {
System.out.println("handlerB:"+request);
handlerNext(request);
}
}
测试:
public static void main(String[] args) {
Handler handlerA = new HandlerA();
Handler handlerB = new HandlerB();
handlerA.setNextHandler(handlerB);
handlerA.handleRequest("hello chain");
}
时序图
使用责任链模式解决实际问题
业务代码
下面用户登陆的伪代码,流程如下:非空判断-->用户存在性判断-->权限角色判断,包括我目前所在的公司的代码也是这样,不过比下面的代码长多了,好几百行,十分臃肿,我们可以考虑使用责任链模式,将所有步骤串联起来,可以使我们在具体编码时更加专注于某一个具体的业务逻辑处理。
@Data
public class Member {
private String name;
private String password;
private String roleName;
public Member(String name, String password) {
this.name = name;
this.password = password;
}
}
public class MemberService {
public void login(String name, String password){
if(StringUtils.isEmpty(name) || StringUtils.isEmpty(password)){
throw new RuntimeException("密码或账号为空,校验失败");
}
Member member = checkExist(name, password);
if(member==null){
throw new RuntimeException("用户不存在");
}
if(!"admin".equals(member.getRoleName())){
throw new RuntimeException("权限不足,非法操作");
}
System.out.println("允许操作");
}
/**
* 模拟查询数据库
*/
private Member checkExist(String name, String password) {
if("admin".equals(name) && "123456".equals(password)){
Member member = new Member(name, password);
member.setRoleName("admin");
return member;
}
return null;
}
}
改造成责任链模式
public abstract class Handler {
protected Handler chain;
public void nextChain(Handler handler){
this.chain = handler;
}
public abstract void doHandle(Member member);
}
public class ValidateHandler extends Handler{
@Override
public void doHandle(Member member) {
if(StringUtils.isEmpty(member.getName()) || StringUtils.isEmpty(member.getPassword())){
throw new RuntimeException("密码或账号为空,校验失败");
}
chain.doHandle(member);
}
}
public class LoginHandler extends Handler{
@Override
public void doHandle(Member member) {
if("admin".equals(member.getName()) && "123456".equals(member.getPassword())){
member.setRoleName("admin");
chain.doHandle(member);
}else{
throw new RuntimeException("用户不存在");
}
}
}
public class AuthHandler extends Handler{
@Override
public void doHandle(Member member) {
if(!"admin".equals(member.getRoleName())){
throw new RuntimeException("权限不足,非法操作");
}
System.out.println("允许操作");
}
}
测试:
Handler validateHandler = new ValidateHandler();
Handler loginHandler = new LoginHandler();
Handler authHandler = new AuthHandler();
validateHandler.nextChain(loginHandler);
loginHandler.nextChain(authHandler);
Member member = new Member("admin", "123456");
validateHandler.doHandle(member);
责任链模式结合建造者模式
上述代码有一个问题,具体组装链式结构的角色是MemberService类,当链式结构较长时,MemberService的工作会异常繁琐,且代码臃肿,产生问题原因是链式结构组装非常复杂,而对于复杂对象的创建,我们很自然想到建造者模式,我们可以改造Handler代码。
public abstract class Handler {
protected Handler chain;
public void nextChain(Handler handler){
this.chain = handler;
}
public abstract void doHandle(Member member);
public static class Builder{
private Handler head;
private Handler tail;
public Builder addHandler(Handler handler){
if(head==null){
this.head = this.tail = handler;
return this;
}
this.tail.nextChain(handler);
this.tail = handler;
return this;
}
public Handler build(){
return this.head;
}
}
}
测试:
Member member = new Member("admin", "123456");
Handler ha = new Handler.Builder()
.addHandler(new ValidateHandler())
.addHandler(new LoginHandler())
.addHandler(new AuthHandler())
.build();
ha.doHandle(member);
这样的代码写法就比上一种要优美的多。
责任链模式优缺点
优点
- 请求与处理解耦
- 请求处理者只需要关注子的感兴趣的进行处理即可,对于不感兴趣的请求,直接转发给下一个节点对象
- 具备链式传递处理请求功能,请求发送者不需要知晓链路结构,只需要等待请求处理结果即可。
- 链路结构灵活,可以通过改变链路结构动态新增或删减责任。
- 易于扩展新的请求处理类,符合开闭原则
缺点
- 责任链过长或处理时间过长,会影响整体性能
- 如果节点对象存在循环引用,则会造成死循环