• 设计模式学习总结:责任链模式


    ​ 本文为笔者在阅读一些书籍、博客、专栏等资料后所总结的个人对于责任链模式的笔记,由于笔者才疏学浅,若有不足之处,还望各位加以斧正,您的建议与鼓励都是笔者源源不断的前进动力。感谢!

    文章大纲如下:

    • 责任链模式的简单认识
    • 责任链模式的简单应用
      • 两种责任链的实现方法:基于数组、链表
      • 责任链的一些简单应用
    • 责任链模式在源码中的体现
    • 责任链模式的优缺点

    责任链模式简单认识

    Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. ---- 《设计模式》GoF

    中文意思为:

    通过给多个对象处理请求的机会,避免请求的发送方与其接收方耦合。将接收对象串起来,并沿着链传递请求,直到有一个对象处理它。

    笔者的将其理解为:

    现在有一条责任链(其中包含着各种接收对象,也可以理解为处理器,其具有处理请求的方法)。发送方对责任链发送请求,责任链中的对象A接收到请求后,若其能处理该请求,则处理,否则就向下一个对象传递请求,直到有一个对象能对其进行处理。整个过程形成一条链。

    此外,责任链模式还有另一种实现方式,即

    请求进入责任链,对象A接收到请求后,若需要处理,则处理,处理后传递给下一个对象;若不需要处理,则直接传递给下一个对象。也就是说,其最终都会将该对象传递给链上的下一个对象,以此类推,直到最后一个对象处理后,方结束。此变体中的请求会被责任链中的每个处理器(对象)处理。

    责任链模式的简单应用

    这两种责任链的实现方法

    以下实现代码参考自王争先生在极客时间上的《设计模式之美》所展示的代码片段。我在其中注释部分加入了自己的理解、总结。

    第一种责任链(即处理后就返回)

    此责任链的实现方法有两种,分别是数组实现和链表实现。

    • 数组实现

    数组实现的大致模板:

    /**
     * 处理者接口,定义了处理者的行为
     */
    interface IHandler {
        boolean handle();
    }
    
    /**
     * 处理者A
     */
    class HandlerA implements IHandler  {
        @Override
        public boolean handle() {
            boolean status = true;
            // 此处省略处理任务的方法……
            // 如果任务被处理了,就令status为true;否则令其为false
            System.out.println("[HandlerA]: status is " + status);
            return status;
        }
    }
    
    /**
     * 处理者B
     */
    class HandlerB implements IHandler  {
        @Override
        public boolean handle() {
            boolean status = true;
            // 此处省略处理任务的方法……
            // 如果任务被处理了,就令status为true;否则令其为false
            System.out.println("[HandlerB]: status is " + status);
            return status;
        }
    }
    
    /**
     * 责任链
     */
    class HandlerChain {
        // 责任链的数组存储对象
        private List<IHandler> handlerList = new ArrayList<>();
        
        // 向数组中添加handler的方法,添加成功返回true,反之为false
        public boolean addHandler(IHandler handler) {
            return this.handlerList.add(handler);
        }
        
        // 责任链调用处理者去处理请求
        public void handle() {
            // 遍历责任链上的处理者
            for (IHandler handler : handlerList) {
                boolean status = handler.handle();
                // 如果处理者返回的是true,就说明已经处理请求了,可以退出遍历了
                // 反之,继续遍历,直到被处理
                if (status == true) {
                    break;
                }
            }
        }
    }
    
    /**
     * 基于数组实现的责任链(处理后就停止的责任链)
     */
    public class Demo {
        public static void main(String[] args) {
            HandlerChain handlerChain = new HandlerChain();
            handlerChain.addHandler(new HandlerA());
            handlerChain.addHandler(new HandlerB());
            handlerChain.handle();
        }
    }
    

    输出结果为:

    可见,HandleA处理了请求后,就不再往后传递请求了。

    • 链表实现

    链表实现的大致模板:

    /**
     * 处理者抽象类,定义了处理者的行为
     */
    abstract class IHandler {
        // 下一个处理者
        protected IHandler nextHandler;
    
        // 子类需要实现的处理方法
        protected abstract boolean doHandle();
    
        // 设置下一个处理者
        public void setNextHandler(IHandler nextHandler) {
            this.nextHandler = nextHandler;
        }
    
        // 获取下一个处理者
        public IHandler getNextHandler() {
            return this.nextHandler;
        }
    
        // 父类调用子类的处理方法判断是否向后传递请求
        public final void handle() {
            boolean status = doHandle();
            if ((!status) && (this.nextHandler != null)) {
                this.nextHandler.handle();
            }
        }
    }
    
    /**
     * 处理者A
     */
    class HandlerA extends IHandler  {
        @Override
        public boolean doHandle() {
            boolean status = false;
            // 此处省略处理任务的方法……
            // 如果任务被处理了,就令status为true;否则令其为false
            System.out.println("[HandlerA]: status is " + status);
            return status;
        }
    }
    
    /**
     * 处理者B
     */
    class HandlerB extends IHandler  {
        @Override
        public boolean doHandle() {
            boolean status = true;
            // 此处省略处理任务的方法……
            // 如果任务被处理了,就令status为true;否则令其为false
            System.out.println("[HandlerB]: status is " + status);
            return status;
        }
    }
    
    /**
     * 责任链
     */
    class HandlerChain {
        // 责任链的头结点
        private IHandler headHandler;
        // 责任链的尾结点
        private IHandler tailHandler;
    
        // 向责任链中添加处理者
        public void addHandler(IHandler handler) {
            // 把处理者的下一个结点设为空
            handler.setNextHandler(null);
            // 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
            if (this.headHandler == null) {
                this.headHandler = handler;
                this.tailHandler = handler;
                return; // 设置好后就返回,因为后面要给head非空时的情况赋值
            }
            // headHandler非空时,把传进来的结点加入到链表尾部
            this.tailHandler.setNextHandler(handler); 
            this.tailHandler = handler;
        }
    
        // 责任链调用处理者去处理请求
        public void handle() {
            if (this.headHandler != null) {
                this.headHandler.handle();
            }
        }
    }
    
    /**
     * 基于链表实现的责任链(处理后就停止的责任链)
     */
    public class Demo {
        public static void main(String[] args) {
            HandlerChain handlerChain = new HandlerChain();
            handlerChain.addHandler(new HandlerA());
            handlerChain.addHandler(new HandlerB());
            handlerChain.handle();
        }
    }
    

    输出结果:

    第二种责任链(一直往后传递)

    此责任链的实现方法也是两种,即数组、链表实现。

    • 数组实现

    数组实现的大致模板:

    /**
     * 处理者接口,定义了处理者的行为
     */
    interface IHandler {
        // 实现类的处理方法
        void handle();
    }
    
    /**
     * 处理者A
     */
    class HandlerA implements IHandler {
        @Override
        public void handle() {
            // 此处省略处理任务的方法…
            System.out.println("[HandlerA]: handle()");
        }
    }
    
    /**
     * 处理者B
     */
    class HandlerB implements IHandler {
        @Override
        public void handle() {
            // 此处省略处理任务的方法……
            System.out.println("[HandlerB]: handle()");
        }
    }
    
    /**
     * 责任链
     */
    class HandlerChain {
        // 责任链的存储对象
        List<IHandler> handlerList = new ArrayList<>();
    
        // 向责任链中添加处理者
        public HandlerChain addHandler(IHandler handler) {
            handlerList.add(handler);
            return this;
        }
    
        // 责任链调用处理者去处理请求
        public void handle() {
            for (IHandler handler : handlerList) {
                handler.handle();
            }
        }
    }
    
    /**
     * 基于数组实现的责任链(处理后就停止的责任链)
     */
    public class Demo {
        public static void main(String[] args) {
            HandlerChain handlerChain = new HandlerChain();
            IHandler handlerA = new HandlerA();
            IHandler handlerB = new HandlerB();
            handlerChain.addHandler(handlerA).addHandler(handlerB);
            handlerChain.handle();
        }
    }
    

    输出结果:

    • 链表实现

    链表实现的大致模板:

    /**
     * 处理者抽象类,定义了处理者的行为
     */
    abstract class IHandler {
        // 下一个处理者
        protected IHandler nextHandler;
    
        // 子类需要实现的处理方法
        protected abstract void doHandle();
    
        // 设置下一个处理者
        public void setNextHandler(IHandler nextHandler) {
            this.nextHandler = nextHandler;
        }
    
        // 获取下一个处理者
        public IHandler getNextHandler() {
            return this.nextHandler;
        }
    
        // 父类调用子类的处理方法并向后请求
        public final void handle() {
            doHandle();
            // 与前一种责任链所不同的是,这里不需要通过前面是否处理判断是否传递,而是一直传递
            if (this.nextHandler != null) {
                System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
                this.nextHandler.handle();
            }
        }
    }
    
    /**
     * 处理者A
     */
    class HandlerA extends IHandler  {
        @Override
        public void doHandle() {
            // 此处省略处理任务的方法…
            System.out.println("[HandlerA]: doHandler()");
        }
    }
    
    /**
     * 处理者B
     */
    class HandlerB extends IHandler  {
        @Override
        public void doHandle() {
            // 此处省略处理任务的方法……
            System.out.println("[HandlerB]: doHandler()");
        }
    }
    
    /**
     * 责任链
     */
    class HandlerChain {
        // 责任链的头结点
        private IHandler headHandler;
        // 责任链的尾结点
        private IHandler tailHandler;
    
        // 向责任链中添加处理者
        public void addHandler(IHandler handler) {
            // 把处理者的下一个结点设为空
            handler.setNextHandler(null);
            // 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
            if (this.headHandler == null) {
                this.headHandler = handler;
                this.tailHandler = handler;
                return; // 设置好后就返回,因为后面要给head非空时的情况赋值
            }
            // headHandler非空时,把传进来的结点加入到链表尾部
            this.tailHandler.setNextHandler(handler);
            this.tailHandler = handler;
        }
    
        // 责任链调用处理者去处理请求
        public void handle() {
            if (this.headHandler != null) {
                this.headHandler.handle();
            }
        }
    }
    
    /**
     * 基于链表实现的责任链(处理后就停止的责任链)
     */
    public class Demo {
        public static void main(String[] args) {
            HandlerChain handlerChain = new HandlerChain();
            handlerChain.addHandler(new HandlerA());
            handlerChain.addHandler(new HandlerB());
            handlerChain.handle();
        }
    }
    

    输出结果:

    可以看到,HandlerA在处理了请求后,又传递给了HandlerB,HandlerB也对请求进行了处理。

    责任链的一些应用

    一个小案例

    假设有一场景如下:我们需要对用户的登录请求进行登陆前的数据校验、登录表单的数据校验、权限数据的校验。

    在这样的场景中,我们通常会这样去实现(模拟实现,省略了部分操作、代码):

    public class Demo {
        public boolean login(User user) {
            String userName = user.getUserName();
            String password = user.getPassword();
            // 非空校验(数据校验部分)
            if (StringUtils.isEmpty(userName) && StringUtils.isEmpty(password)) {
                System.out.println("数据校验不通过!");
                return false;
            }
            System.out.println("数据校验通过!");
    
            // 用户名、密码校验,模拟验证
            if (userName != "xingzhi" && password != "987") {
                System.out.println("用户名或密码错误!");
                return false;
            }
            System.out.println("用户名、密码校验通过!");
            user.setRoleName("visitor");
    
            // 用户角色校验
            if (user.getRoleName() != "admin") {
                System.out.println("无权限登录!");
                return false;
            }
            System.out.println("权限验证通过");
            return true;
        }
    
        // 模拟登录接口调用
        public static void main(String[] args) {
            Demo demo = new Demo();
            User user = new User().setUserName("xingzhi").setPassword("987");
            if (demo.login(user)) {
                System.out.println("登陆成功!");
            } else {
                System.out.println("登陆失败!");
            }
        }
    }
    
    // 模拟一个用户类
    class User {
        private String userName;
        private String password;
        private String roleName;
    
        public String getUserName() {
            return userName;
        }
    
        public User setUserName(String userName) {
            this.userName = userName;
            return this;
        }
    
        public String getPassword() {
            return password;
        }
    
        public User setPassword(String password) {
            this.password = password;
            return this;
        }
    
        public String getRoleName() {
            return roleName;
        }
    
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
    }
    

    输出结果如下:

    这样确实把需求实现了,但是

    根据这一场景,责任链模式是一个很好的选择。因为进行完一个校验后还需要后续的校验,所以我们选择使用第二种责任链的链表实现方式来完成需求(需要注意的是,当某一处理者不通过时,就不往下传递了,所以我在Ihandler接口类中新增了handlerStatus属性用来记录处理者的处理状态)。代码如下:(但我感觉我这个案例可能不太好.....)

    /** 处理者父类 */
    abstract class abstractHandler {
        // 下一个处理者
        protected abstractHandler nextHandler;
        protected boolean handlerStatus;
    
        // 子类需要实现的处理方法
        protected abstract boolean doHandle(User user);
    
        // 设置下一个处理者
        public void setNextHandler(abstractHandler nextHandler) {
            this.nextHandler = nextHandler;
        }
    
        // 获取下一个处理者
        public abstractHandler getNextHandler() {
            return this.nextHandler;
        }
    
        // 父类调用子类的处理方法并向后请求
        public final boolean handle(User user) {
            handlerStatus = doHandle(user);
            if (!handlerStatus) return false;
            // 与前一种责任链所不同的是,这里不需要通过前面是否处理判断是否传递,而是一直传递
            if (this.nextHandler != null) {
                System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
                this.nextHandler.handle(user);
            }
            return true;
        }
    }
    
    /**
     * 数据校验处理者
     */
    class ValidateHandler extends abstractHandler {
        @Override
        public boolean doHandle(User user) {
            System.out.println("[ValidateHandler]: doHandler()");
            // 如果用户名和密码不为空,则进行处理
            if (!StringUtils.isEmpty(user.getUserName()) && !StringUtils.isEmpty(user.getPassword())) {
                System.out.println("数据校验成功!");
                return true;
            } else {
                System.out.println("数据格式不通过!");
                return false;
            }
        }
    }
    
    /**
     * 身份校验处理者
     */
    class AuthenticationHandler extends abstractHandler {
        @Override
        public boolean doHandle(User user) {
            System.out.println("[AuthenticationHandler]: doHandler()");
            if ("xingzhi".equals(user.getUserName()) && "987".equals(user.getPassword())) {
                user.setRoleName("admin");
                System.out.println("身份验证通过!");
                return true;
            } else {
                System.out.println("身份验证不通过!");
                return false;
            }
        }
    }
    
    /** 角色校验处理者 */
    class RoleHandler extends abstractHandler {
        @Override
        public boolean doHandle(User user) {
            System.out.println("[RoleHandler]: doHandler()");
            if ("admin".equals(user.getRoleName())) {
                System.out.println("橘色校验通过!");
                return true;
            } else {
                System.out.println("角色校验不通过!");
                return false;
            }
        }
    }
    
    /**
     * 责任链
     */
    class LoginHandlerChain {
        // 责任链的头结点
        private abstractHandler headHandler;
        // 责任链的尾结点
        private abstractHandler tailHandler;
    
        // 向责任链中添加处理者
        public LoginHandlerChain addHandler(abstractHandler handler) {
            // 把处理者的下一个结点设为空
            handler.setNextHandler(null);
            // 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
            if (this.headHandler == null) {
                this.headHandler = handler;
                this.tailHandler = handler;
                return this; // 设置好后就返回,因为后面要给head非空时的情况赋值
            }
            // headHandler非空时,把传进来的结点加入到链表尾部
            this.tailHandler.setNextHandler(handler);
            this.tailHandler = handler;
            return this;
        }
    
        // 责任链调用处理者去处理请求
        public boolean handle(User user) {
            if (this.headHandler != null) {
                return this.headHandler.handle(user);
            }
            return false;
        }
    }
    
    // 模拟一个用户类
    class User {
        private String userName;
        private String password;
        private String roleName;
        private String Permission;
    
        public String getUserName() {
            return userName;
        }
    
        public User setUserName(String userName) {
            this.userName = userName;
            return this;
        }
    
        public String getPassword() {
            return password;
        }
    
        public User setPassword(String password) {
            this.password = password;
            return this;
        }
    
        public String getRoleName() {
            return roleName;
        }
    
        public User setRoleName(String roleName) {
            this.roleName = roleName;
            return this;
        }
    
        public String getPermission() {
            return Permission;
        }
    
        public User setPermission(String permission) {
            Permission = permission;
            return this;
        }
    }
    
    /**
     * 基于链表实现的责任链(处理后就停止的责任链)
     */
    public class Demo {
        public static void main(String[] args) {
            User user = new User()
                    .setUserName("xingzhi")
                    .setPassword("987");
            LoginHandlerChain loginHandlerChain = new LoginHandlerChain()
                    .addHandler(new ValidateHandler())
                    .addHandler(new AuthenticationHandler())
                    .addHandler(new RoleHandler());
            if (loginHandlerChain.handle(user)) {
                System.out.println("登陆成功!欢迎您:" + user.getUserName());
            } else {
                System.out.println("登陆失败!");
            };
        }
    }
    

    运行结果如图:

    在这个场景中使用责任链模式就可以满足开闭原则,提高代码的扩展性。

    比如,现在需要在角色认证后进行权限认证,这时我们只需要增加一个权限验证类就行了,但是我这个User类还得改改....所以我这个案例举得不太好(有点又臭又长了)....仅供参考...哈哈...

    /** 权限校验处理者 */
    class PermissionHandler extends abstractHandler {
        @Override
        public boolean doHandle(User user) {
            System.out.println("[PermissionHandler]: doHandler()");
            if ("menu:add".equals(user.getPermission())) {
                System.out.println("权限校验通过!");
                return true;
            } else {
                System.out.println("权限校验不通过!");
                return false;
            }
        }
    }
    

    责任链模式在源码中的体现

    在看过了我的糟糕的案例后,我们来看看大师的作品,源码中的责任链模式:

    /**
     * 先留个坑...有时间了来填...
     */
    

    责任链模式的优缺点

    最后,总结下责任链模式的优缺点。

    优点

    • 将请求的发送者和接收者解耦。
    • 简化对象,减少代码的复杂性。(处理者不需要知道责任链的内部构造以及如何处理的)
    • 满足开闭原则,提高代码的扩展性。(动态地新增或删除责任)

    缺点

    • 不容易观察运行时的特征,出现问题不好排查。(如果某一节点出现问题,则容易造成系统崩溃)
    • 并不一定保证请求一定会执行。(责任链过长时,请求没被处理或者处理时间过长,会影响整体性能)
  • 相关阅读:
    小学数学计算出题小程序(Excel版)
    网页自动化测试技术---SeleniumBasic(VBA网页外挂)
    ODBC链接数据源(PQ学习)
    WPF动态绑定矢量图标
    由数据转为树杈的js 和由一个子节点的id获取所有的父类的id
    面试上机题目--采用vue实现以下页面效果
    html前端上机面试题
    在vue项目中的跨域解决办法
    vue-quill-editor富文本编辑器使用
    vue项目eslint配置 以及 解释
  • 原文地址:https://www.cnblogs.com/xunxian/p/14135538.html
Copyright © 2020-2023  润新知