设计模式:责任链模式

定义

责任链模式将链中每一个节点都看作一个对象,每个节点处理的请求都不同,且内部自动维护下一个节点对象,当一个请求从链式的首段发出时,会沿着责任链预设的路径依次传递到每一个节点对象,直至被链中的某个对象处理为止,属于行为型设计模式。

使用场景:

  • 多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定
  • 在不明确指定接收者的情况下,向多个对象中的一个提交请求
  • 可以动态指定一组对象处理请求

通用写法

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");
    }

image-20210104194256859

时序图

image-20210104194641731

使用责任链模式解决实际问题

业务代码

下面用户登陆的伪代码,流程如下:非空判断-->用户存在性判断-->权限角色判断,包括我目前所在的公司的代码也是这样,不过比下面的代码长多了,好几百行,十分臃肿,我们可以考虑使用责任链模式,将所有步骤串联起来,可以使我们在具体编码时更加专注于某一个具体的业务逻辑处理。

@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);

这样的代码写法就比上一种要优美的多。

责任链模式优缺点

优点

  • 请求与处理解耦
  • 请求处理者只需要关注子的感兴趣的进行处理即可,对于不感兴趣的请求,直接转发给下一个节点对象
  • 具备链式传递处理请求功能,请求发送者不需要知晓链路结构,只需要等待请求处理结果即可。
  • 链路结构灵活,可以通过改变链路结构动态新增或删减责任。
  • 易于扩展新的请求处理类,符合开闭原则

缺点

  • 责任链过长或处理时间过长,会影响整体性能
  • 如果节点对象存在循环引用,则会造成死循环
原文地址:https://www.cnblogs.com/wwjj4811/p/14232310.html