• 北风设计模式课程---责任链模式 总结


    北风设计模式课程---责任链模式 总结

    一、总结

    一句话总结:

    设置了级别的场景:责任链适合那些已经设置了级别的场景,
    都有对应的抽象类:面向对象这些设计模式,处理类或者工厂类都有对应的抽象类,比如这里责任链模式的 处理类的抽象父类,作用自然是满足 开闭原则。

    1、责任链模式 比正常例子可爱一点的 例子?

    古代女子逛街请示:古代女子有“三从四德”之说,“三从”即指“未嫁从父、既嫁从夫、夫死从子”。举例来说,如果一位女性要出去逛街,在她出嫁前必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?那就得问问儿子是否允许自己出去逛街。

    古代女子有“三从四德”之说,“三从”即指“未嫁从父、既嫁从夫、夫死从子”。也就是说,一位女性在结婚之前要听从于父亲,结婚之后要听从于丈夫,如果丈夫死了还要听从于儿子。举例来说,如果一位女性要出去逛街,在她出嫁前必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?那就得问问儿子是否允许自己出去逛街。估计你接下来马上要问:“要是没有儿子怎么办?”那就请示小叔子、侄子等。在父系社会中,妇女只占从属地位,现在想想中国古代的妇女还是挺悲惨的,连逛街都要多番请示。作为父亲、丈夫或儿子,只有两种选择:要不承担起责任来,允许她或不允许她逛街;要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则。

    2、如下设计解决责任链模式的方法的缺点是什么?

    |||-begin

    class Client {
        public static void main(String[] args) {
            // 随机挑选几个女性
            Random rand = new Random();
            ArrayList<IWoman> arrayList = new ArrayList();
            for (int i = 0; i < 5; i++) {
                arrayList.add(new Woman(rand.nextInt(4), "我要出去逛街"));
            }
            // 定义三个请示对象
            IHandler father = new Father();
            IHandler husband = new Husband();
            IHandler son = new Son();
            for (IWoman woman : arrayList) {
                if (woman.getType() == 1) { // 未结婚少女, 请示父亲
                    System.out.println("
    --------女儿向父亲请示-------");
                    father.HandleMessage(woman);
                } else if (woman.getType() == 2) { // 已婚女子, 请示丈夫
                    System.out.println("
    --------妻子向丈夫请示-------");
                    husband.HandleMessage(woman);
                } else if (woman.getType() == 3) { // 母亲请示儿子
                    System.out.println("
    --------母亲向儿子请示-------");
                    son.HandleMessage(woman);
                } else {
                    // 暂时什么也不做
                }
            }
        }
    }

    |||-end

    职责界定不清晰:对女儿提出的请示,应该在父亲类中做出决定,因此 Father 类应该是知道女儿的请求自己处理,而不是在 Client 类中进行组装,不应该是这样的。
    代码臃肿:我们在 Client 类中写了很多 if...else 的判断条件,而且能随着能处理该类型的请示人员越多,if...else 的判断就越多,代码就越来越臃肿,可读性就越来越低。
    耦合严重:这是什么意思呢,我们要根据 Woman 的 type 来决定使用 IHandler 的那个实现类来处理请求。有一个问题是:如果 IHandler 的实现类继续扩展怎么办?修改 Client 类?这与开闭原则违背。
    异常情况欠考虑:女子提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说是父亲作出一个决断,而丈夫也作出了一个决断,正确的顺序应该是请求一层一层的传递的,父亲处理过后再由丈夫处理。

    3、责任链抽象Handle类实例?

    |||-begin

    public abstract class Handler {
        public final static int FATHER_LEVEL_REQUEST = 1;
        public final static int HUSBAND_LEVEL_REQUEST = 2;
        public final static int SON_LEVEL_REQUEST = 3;
        // 能处理的级别
        private int level = 0;
        // 责任传递, 下一个人责任人是谁
        private Handler nextHandler;
     
        // 每个类都要说明一下自己能处理哪些请求
        public Handler(int _level) {
            this.level = _level;
        }
     
        // 你要处理这个请求
        public final void HandleMessage(IWoman woman) {
            if (woman.getType() == this.level) {
                this.response(woman);
            } else {
                if (this.nextHandler != null) { // 有后续环节,
                    this.nextHandler.HandleMessage(woman); // 才把请求往后递送this.nextHandler.HandleMessage(women);
                } else { // 已经没有后续处理人了, 不用处理了
                    System.out.println("---没地方请示了, 按不同意处理---
    ");
                }
            }
        }
     
        // 如果不属于你处理的请求, 你应该让她找下一个环节的人, 如女儿出嫁了, 还向父亲请示是否可以逛街, 那父亲就应该告诉女儿, 应该找丈夫请示
        public void setNext(Handler _nextHandler) {
            this.nextHandler = _nextHandler;
        }
     
        // 有请示那当然要回应
        protected abstract void response(IWoman woman);
    }

    |||-end

    有级别 ,有处理请求,有找自己的下一级
    找下一级的操作就是:设置(获取)下一级的对象:this.nextHandler = _nextHandler;

    4、责任链 设计模式 优缺点?

    优点:将请求者和处理者分开,两者解耦,提供系统灵活性。
    缺点:请求都是从链头遍历到链尾,当链很长的时候,性能是个很大的问题。

    二、责任链模式(十二)

    转自或参考:责任链模式(十二)
    https://blog.csdn.net/afei__/article/details/80677711

     

    一、引子

    古代女子有“三从四德”之说,“三从”即指“未嫁从父、既嫁从夫、夫死从子”。也就是说,一位女性在结婚之前要听从于父亲,结婚之后要听从于丈夫,如果丈夫死了还要听从于儿子。举例来说,如果一位女性要出去逛街,在她出嫁前必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?那就得问问儿子是否允许自己出去逛街。估计你接下来马上要问:“要是没有儿子怎么办?”那就请示小叔子、侄子等。在父系社会中,妇女只占从属地位,现在想想中国古代的妇女还是挺悲惨的,连逛街都要多番请示。作为父亲、丈夫或儿子,只有两种选择:要不承担起责任来,允许她或不允许她逛街;要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则。

    1. 针对这个例子,我们可以先定义一个女子的接口,如下:
    public interface IWoman {
        int getType();
        // 获得个人请示, 你要干什么? 出去逛街? 约会?还是看电影?
        String getRequest();
    }
    2. 然后定义一个古代女子的类,实现这个接口
    public class Woman implements IWoman {
     
        // 通过一个int类型的参数来描述妇女的个人状况 1--未出嫁 2--出嫁 3--夫死
        private int type = 0;
        // 女子的请示
        private String request = "";
     
        // 构造函数传递过来请求
        public Woman(int _type, String _request) {
            this.type = _type;
            this.request = _request;
        }
     
        // 获得自己的状况
        public int getType() {
            return this.type;
        }
     
        // 获得妇女的请求
        public String getRequest() {
            return this.request;
        }
    }

    二、错误示范

    假如你之前并不知道责任链模式,你可能会如下设计:

    1. 父亲、丈夫、儿子都是处理者,可以这么定义
    public interface IHandler {
        // 你要处理这个请求
        void HandleMessage(IWoman woman);
    }
     
    public class Father implements IHandler {
        // 未出嫁的女儿请示父亲
        public void HandleMessage(IWoman woman) {
            System.out.println("父亲的答复是:同意");
        }
    }
     
    public class Husband implements IHandler {
        // 妻子请示丈夫
        public void HandleMessage(IWoman woman) {
            System.out.println("丈夫的答复是: 同意");
        }
    }
     
    public class Son implements IHandler {
        // 母亲请示儿子
        public void HandleMessage(IWoman woman) {
            System.out.println("儿子的答复是: 同意");
        }
    }
    2. 如上,还差一个场景类来模拟这个场景了
    class Client {
        public static void main(String[] args) {
            // 随机挑选几个女性
            Random rand = new Random();
            ArrayList<IWoman> arrayList = new ArrayList();
            for (int i = 0; i < 5; i++) {
                arrayList.add(new Woman(rand.nextInt(4), "我要出去逛街"));
            }
            // 定义三个请示对象
            IHandler father = new Father();
            IHandler husband = new Husband();
            IHandler son = new Son();
            for (IWoman woman : arrayList) {
                if (woman.getType() == 1) { // 未结婚少女, 请示父亲
                    System.out.println("
    --------女儿向父亲请示-------");
                    father.HandleMessage(woman);
                } else if (woman.getType() == 2) { // 已婚女子, 请示丈夫
                    System.out.println("
    --------妻子向丈夫请示-------");
                    husband.HandleMessage(woman);
                } else if (woman.getType() == 3) { // 母亲请示儿子
                    System.out.println("
    --------母亲向儿子请示-------");
                    son.HandleMessage(woman);
                } else {
                    // 暂时什么也不做
                }
            }
        }
    }
    3. 这样也可以正常运行,但是会存在以下几个问题:
    • 职责界定不清晰
      对女儿提出的请示,应该在父亲类中做出决定,因此 Father 类应该是知道女儿的请求自己处理,而不是在 Client 类中进行组装,不应该是这样的。
    • 代码臃肿
      我们在 Client 类中写了很多 if...else 的判断条件,而且能随着能处理该类型的请示人员越多,if...else 的判断就越多,代码就越来越臃肿,可读性就越来越低。
    • 耦合严重
      这是什么意思呢,我们要根据 Womantype 来决定使用 IHandler 的那个实现类来处理请求。有一个问题是:如果 IHandler 的实现类继续扩展怎么办?修改 Client 类?这与开闭原则违背。
    • 异常情况欠考虑
      女子提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说是父亲作出一个决断,而丈夫也作出了一个决断,正确的顺序应该是请求一层一层的传递的,父亲处理过后再由丈夫处理。

    三、正确示范

    虽然你还不知道责任链模式是怎样定义的,但是哦我们可以先来看一下它可以怎样优雅地处理上面这种情况。

    1. 首先重新定义一个抽象的 Handler
    public abstract class Handler {
        public final static int FATHER_LEVEL_REQUEST = 1;
        public final static int HUSBAND_LEVEL_REQUEST = 2;
        public final static int SON_LEVEL_REQUEST = 3;
        // 能处理的级别
        private int level = 0;
        // 责任传递, 下一个人责任人是谁
        private Handler nextHandler;
     
        // 每个类都要说明一下自己能处理哪些请求
        public Handler(int _level) {
            this.level = _level;
        }
     
        // 你要处理这个请求
        public final void HandleMessage(IWoman woman) {
            if (woman.getType() == this.level) {
                this.response(woman);
            } else {
                if (this.nextHandler != null) { // 有后续环节,
                    this.nextHandler.HandleMessage(woman); // 才把请求往后递送this.nextHandler.HandleMessage(women);
                } else { // 已经没有后续处理人了, 不用处理了
                    System.out.println("---没地方请示了, 按不同意处理---
    ");
                }
            }
        }
     
        // 如果不属于你处理的请求, 你应该让她找下一个环节的人, 如女儿出嫁了, 还向父亲请示是否可以逛街, 那父亲就应该告诉女儿, 应该找丈夫请示
        public void setNext(Handler _nextHandler) {
            this.nextHandler = _nextHandler;
        }
     
        // 有请示那当然要回应
        protected abstract void response(IWoman woman);
    }
    2. 定义具体的实现类
    public class Father extends Handler {
        // 父亲只处理女儿的请求
        public Father() {
            super(Handler.FATHER_LEVEL_REQUEST);
        }
     
        protected void response(IWoman woman) {
            System.out.println("父亲的答复是:同意
    ");
        }
    }
     
    public class Husband extends Handler {
        // 丈夫只处理妻子的请求
        public Husband() {
            super(Handler.HUSBAND_LEVEL_REQUEST);
        }
     
        protected void response(IWoman woman) {
            System.out.println("丈夫的答复是: 同意
    ");
        }
    }
     
    public class Son extends Handler {
        // 儿子只处理母亲的请求
        public Son() {
            super(Handler.SON_LEVEL_REQUEST);
        }
     
        protected void response(IWoman woman) {
            System.out.println("儿子的答复是: 同意
    ");
        }
    }
    3. 模拟一个场景
    class Client {
        public static void main(String[] args) {
            // 随机挑选几个女性
            Random rand = new Random();
            ArrayList<IWoman> arrayList = new ArrayList();
            for (int i = 0; i < 5; i++) {
                arrayList.add(new Woman(rand.nextInt(4), "我要出去逛街"));
            }
            // 定义三个请示对象
            Handler father = new Father();
            Handler husband = new Husband();
            Handler son = new Son();
            // 设置请示顺序
            father.setNext(husband);
            husband.setNext(son);
            for (IWoman woman : arrayList) {
                father.HandleMessage(woman);
            }
        }
    }
    4. 分析

    结果也正确,业务调用类 Client 也不用去做判断到底是需要谁去处理,而且 Handler 抽象类的子类可以继续增加下去,只需要扩展传递链而已,调用类可以不用了解变化过程,甚至是谁在处理这个请求都不用知道。在这种模式下,该父亲处理就父亲处理,不该父亲处理就往下传递。 这就是责任链模式。

     

    四、定义

    使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

     

    五、类图及通用模版

    一种通用模版的写法如下:

    1. 抽象处理者
    public abstract class Handler {
     
        private Handler nextHandler;
     
        // 每个处理者都必须对请求做出处理
        public final Response handleMessage(Request request) {
            Response response = null;
            // 判断是否是自己的处理级别
            if (this.getHandlerLevel().equals(request.getRequestLevel())) {
                response = this.echo(request);
            } else { // 不属于自己的处理级别
                // 判断是否有下一个处理者
                if (this.nextHandler != null) {
                    response = this.nextHandler.handleMessage(request);
                } else {
                    // 没有适当的处理者, 业务自行处理
                }
            }
            return response;
        }
     
        // 设置下一个处理者是谁
        public void setNext(Handler _handler) {
            this.nextHandler = _handler;
        }
     
        // 每个处理者都有一个处理级别
        protected abstract Level getHandlerLevel();
     
        // 每个处理者都必须实现处理任务
        protected abstract Response echo(Request request);
    }

    这里抽象的处理者实现三个职责:一是定义一个请求的处理方法 handleMessage,也是唯一对外开放的方法;二是定义一个链的编排方法 setNext,设置下一个处理者; 三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别 getHandlerLevel 和具体的处理任务 echo
    注意这里 handleMessage 方法前面的 final 关键字,这里其实就是模版方法的一个使用,父类定义了部分方法的执行顺序,实际实现却是子类完成。

    2. 其它相关接口类
    public interface Request {
        // 获取请求的等级
        Level getRequestLevel();
    }
     
     
    public interface Response {
        // 返回的结果
    }
     
     
    public interface Level {
        // 等级的定义
    }
    3. 场景类
    class Client {
        public static void main(String[] args) {
            // 声明所有的处理节点
            Handler handler1 = new ConcreteHandler1();
            Handler handler2 = new ConcreteHandler2();
            Handler handler3 = new ConcreteHandler3();
            // 设置链中的阶段顺序1-->2-->3
            handler1.setNext(handler2);
            handler2.setNext(handler3);
            Request request = new ConcreteRequest();
            // 提交请求, 返回结果
            Response response = handler1.handleMessage(request);
        }
    }

    其实在实际应用中,一般还有有一个类来封装责任链,完成责任链的顺序编排,并返回链中的第一个处理者,这样上层逻辑就不再需要设置具体的责任链了,简化了上层逻辑,减少了模块间的耦合,系统也会更灵活。

     

    六、优缺点

    • 优点
      将请求者和处理者分开,两者解耦,提供系统灵活性。
    • 缺点
      请求都是从链头遍历到链尾,当链很长的时候,性能是个很大的问题。
      一般我们可以在 Handler 中设置一个最大节点数量,在 setNext 方法中判断是否已经超过最大节点数,超过则不允许继续添加处理者,避免无意识的破坏系统性能。
     
  • 相关阅读:
    45到数据库查询题
    Error: Could not link: /usr/local/share/doc/homebrew
    根据两点坐标,计算连线与坐标轴间的夹角(弧度、角度)
    excel2json
    Mac下的unity兼容问题,打开项目提示错误:!GetPersistentManager().IsStreamLoaded(assetPath)
    Linker Command failed with exit code 1
    module.exports与exports区别
    Nginx配置SSL证书部署HTTPS方法
    Option path is not valid. Please refer to the README.
    javascript中call()、apply()、bind()的用法终于理解
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/11073444.html
Copyright © 2020-2023  润新知