• 王争 | 设计模式之美 - 职责链模式


    1. 职责链模式的定义

    将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

    在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。

    2. 职责链模式的代码实现方式1

    关于职责链模式,我们先来看看它的代码实现。结合代码实现,你会更容易理解它的定义。职责链模式有多种实现方式,我们这里介绍两种比较常用的。

    1. 处理器抽象类 Handler

    Handler 是所有处理器类的抽象父类,handle() 是抽象方法

    public abstract class Handler {
        /**
         *  责任传递,下一个处理器
         */
        protected Handler successor = null;
    
        /**
         * 每个处理者实现类都需要定义下一个处理器是谁
         *
         * @param successor 下一个处理器
         */
        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }
    
        /**
         * 抽象方法
         */
        public abstract void handle();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    2. 具体处理器 HandlerA 和 HandlerB

    每个具体的处理器类(HandlerA、HandlerB)的 handle() 函数的代码结构类似,如果它能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理(也就是调用 successor.handle())。

    public class HandlerA extends Handler {
    
        @Override
        public void handle() {
            // 无法处理
            boolean handled = false;
            // ...
            
            // 如果当前HandlerA无法处理,则设置下一个处理器
            if (!handled && successor != null) {
                successor.handle();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    public class HandlerB extends Handler {
        @Override
        public void handle() {
            // 无法处理
            boolean handled = false;
            // ...
            
            // 如果当前HandlerB无法处理,则设置下一个处理器
            if (!handled && successor != null) {
                successor.handle();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    3. 处理器链 HandlerChain

    HandlerChain 是处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。

    public class HandlerChain {
    
        private Handler head = null;
        private Handler tail = null;
    
        public void addHandler(Handler handler) {
            handler.setSuccessor(null);
            if (head == null) {
                // 头指针指向当前处置器
                head = handler;
                // 尾指针指向当前处置器
                tail = handler;
                return;
            }
            // 让尾指针的下一个处置者设置为当前处置器
            tail.setSuccessor(handler);
            // 让尾指针指定当前处理者
            tail = handler;
        }
    
        public void handle() {
            // 从头指针指向的处置者开始处理
            if (head != null) {
                head.handle();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    4. 使用处理器链 Application
    public class Application {
        public static void main(String[] args) {
            HandlerChain chain = new HandlerChain();
            chain.addHandler(new HandlerA());
            chain.addHandler(new HandlerB());
            chain.handle();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    实际上,上面的代码实现不够优雅。处理器类的 handle() 函数,不仅包含自己的业务逻辑,还包含对下一个处理器的调用,也就是代码中的 successor.handle()。一个不熟悉这种代码结构的程序员,在添加新的处理器类的时候,很有可能忘记在 handle() 函数中调用successor.handle(),这就会导致代码出现 bug。

    3. 职责链模式的代码实现方式1-优化

    针对这个问题,我们对代码进行重构,利用模板模式,将调用 successor.handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。这样具体的处理器类只需要实现自己的业务逻辑就可以了。重构之后的代码如下所示:

    1. 处理器抽象类 Handler
    public abstract class Handler {
    
        /**
         * 下一个处理器
         */
        protected Handler successor = null;
    
        /**
         * 子类具体处理器必须继承该构造方法设置当前处理器的下一个处理器
         *
         * @param successor 下一个处理器
         */
        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }
    
        /**
         * 被final修饰的方法不能被继承
         */
        public final void handle() {
            // 每个处理器都必须判断当前处理器能够处理
            boolean handled = doHandle();
            // 如果当前处理器不能处理,则父类会自动调用下一个处理器处理
            if (successor != null && !handled) {
                successor.handle();
            }
        }
    
        /**
         * 每个处理器都必须判断当前处理器能够处理
         */
        protected abstract boolean doHandle();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    2. 具体处理器 HandlerA 和 HandlerB
    public class HandlerA extends Handler {
        @Override
        protected boolean doHandle() {
            boolean handled = false;
            // 判断该处理器能否处理
            // ...
            return handled;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    public class HandlerB extends Handler {
        @Override
        protected boolean doHandle() {
            boolean handled = false;
            // ...
            return handled;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    HandlerChain和Application代码不变

    4. 职责链模式的代码实现方式2

    我们再来看第二种实现方式,代码如下所示。这种实现方式更加简单。HandlerChain 类用数组而非链表来保存所有的处理器,并且需要在 HandlerChain 的 handle() 函数中,依次调用每个处理器的 handle() 函数。

    1. 处理器抽象类 IHandler
    public interface IHandler {
        boolean handle();
    }
    
    • 1
    • 2
    • 3
    2. 具体处理器 HandlerA 和 HandlerB
    public class HandlerA implements IHandler {
        @Override
        public boolean handle() {
            boolean handled = false;
            //...
            return handled;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public class HandlerB implements IHandler {
        @Override
        public boolean handle() {
            boolean handled = false;
            //...
            return handled;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    3. 处理器链 HandlerChain
    public class HandlerChain {
    
        // 使用数组而非链表来保存所有的处理器
        private List<IHandler> handlers = new ArrayList<>();
    
        /**
         *  添加处理器
         */
        public void addHandler(IHandler handler) {
            this.handlers.add(handler);
        }
    
        /**
         * 依次调用每个处理器的 handle()函数
         */
        public void handle() {
            for (IHandler handler : handlers) {
                boolean handled = handler.handle();
                if (handled) {
                    break;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    4. 使用处理器链 Application
    public class Application {
        public static void main(String[] args) {
            HandlerChain chain = new HandlerChain();
            chain.addHandler(new HandlerA());
            chain.addHandler(new HandlerB());
            chain.handle();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5. 职责链模式变体代码实现

    在 GoF 给出的定义中,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。这种变体也有两种实现方式:用链表存储处理器和用数组存储处理器,跟上面的两种实现方式类似,只需要稍微修改即可。这里只给出其中一种实现方式,如下所示:

    1. 处理器抽象类 Handler
    public abstract class Handler {
        
        protected Handler successor = null;
    
        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }
    
        public final void handle() {
            // 直接处理,不用判断能不能处理,处理完交给下一个处理
            doHandle();
            if (successor != null) {
                successor.handle();
            }
        }
    
        protected abstract void doHandle();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    2. 具体处理器 HandlerA 和 HandlerB
    public class HandlerA extends Handler {
        @Override
        protected void doHandle() {
            //...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class HandlerB extends Handler {
        @Override
        protected void doHandle() {
            //...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    3. 处理器链 HandlerChain
    public class HandlerChain {
        private Handler head = null;
        private Handler tail = null;
    
        public void addHandler(Handler handler) {
            handler.setSuccessor(null);
            if (head == null) {
                head = handler;
                tail = handler;
                return;
            }
            tail.setSuccessor(handler);
            tail = handler;
        }
    
        public void handle() {
            if (head != null) {
                head.handle();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    4. 使用处理器链 Application
    public class Application {
        public static void main(String[] args) {
            com.hh.test3.HandlerChain chain = new HandlerChain();
            chain.addHandler(new HandlerA());
            chain.addHandler(new HandlerB());
            chain.handle();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    【系统软件】源码编译安装ZLIB——Could NOT find ZLIB(missing:ZLIB_LIBRARY ZLIB_INCLUDE_DIR
    无敌!我用【C语言】手搓出了一个体系完整的【员工管理系统】还能玩游戏听音乐?(超详细,附完整源码)
    MySQL只同步单个表或多个表,非全部同步!
    计算机网络的性能指标
    SDWAN:直接互联网接入 (DIA)
    Redis数据库管理工具Redis Desktop Manager最新中文
    Scala---数据基础
    SQL排序函数详解+案例实战
    计算机教育中缺失的一课,劝学弟学妹们一句,一定要趁早补上,工作后会事半功倍!
    vscode使用git
  • 原文地址:https://blog.csdn.net/qq_42764468/article/details/127597743