读书 | 设计模式之禅 - 责任链模式

news2024/11/24 12:23:11

文章目录

      • 1. 实现古代妇女的“三从”制度
      • 2. 实现古代妇女的“三从”制度-优化
      • 3. 责任链模式的定义

1. 实现古代妇女的“三从”制度

一位女性在结婚之前要听从于父亲,结婚之后要听从于丈夫,如果丈夫死了还要听从于儿子。作为父亲、丈夫或儿子,只有两种选择:要不承担起责任来,允许她或不允许她逛街;要不就让她请示下一个人,这是整个社会体系的约束。

下面来看如何通过程序来实现“三从”,需求很简单:通过程序描述一下古代妇女的“三从”制度。

对应的类图:IHandler是三个有决策权对象的接口,IWomen是女性的代码

在这里插入图片描述

① IWomen 接口

public interface IWomen {
    /**
     * 获得个人状况
     *
     * @return 结婚了还是没结婚、丈夫是否在世
     */
    int getType();

    /**
     * 获得个人请示
     * @return 你要干什么?出去逛街?约会?还是看电影?
     */
    String getRequest();
}

② Women 实现类

@NoArgsConstructor
@AllArgsConstructor
public class Women implements IWomen {
    /*
     * 通过一个int类型的参数来描述妇女的个人状况
     * 1--未出嫁
     * 2--出嫁
     * 3--夫死
     */
    private int type = 0;

    //妇女的请示
    private String request = "";

    @Override
    public int getType() {
        return type;
    }

    @Override
    public String getRequest() {
        return request;
    }
}

③ IHandler 接口

public interface IHandler {
    /**
     * 一个女性(女儿、妻子或者母亲)要求逛街,你要处理这个请求
     */
    public void HandleMessage(IWomen women);
}

④ Father 实现类

public class Father implements IHandler {
    /**
     * 未出嫁的女儿来请示父亲
     */
    public void HandleMessage(IWomen women) {
        System.out.println("女儿的请示是:" + women.getRequest());
        System.out.println("父亲的答复是:同意");
    }
}

⑤ Husband 实现类

public class Husband implements IHandler {
    /**
     * 妻子向丈夫请示
     */
    public void HandleMessage(IWomen women) {
        System.out.println("妻子的请示是:" + women.getRequest());
        System.out.println("丈夫的答复是:同意");
    }
}

⑥ Son 实现类

public class Son implements IHandler {
    /**
     * 母亲向儿子请示
     */
    public void HandleMessage(IWomen women) {
        System.out.println("母亲的请示是:" + women.getRequest());
        System.out.println("儿子的答复是:同意");
    }
}

⑦ Client 测试类

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

上述代码实现存在职责界定不清晰,代码臃肿,耦合过重,异常情况欠考虑等问题。

2. 实现古代妇女的“三从”制度-优化

女性提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说是父亲作出一个决断,而丈夫也作出了一个决断,也即是请示传递出去,必然有一个唯一的处理人给出唯一的答复。

我们可以抽象成这样一个结构,女性的请求先发送到父亲类,父亲类一看是自己要处理的,就作出回应处理,如果女儿已经出嫁了,那就要把这个请求转发到女婿来处理,那女婿一旦去天国报道了,那就由儿子来处理这个请求。

在这里插入图片描述

父亲、丈夫、儿子每个节点有两个选择:要么承担责任,做出回应;要么把请求转发到后序环节。

在这里插入图片描述

三个实现类Father、Husband、Son只要实现构造函数和父类中的抽象方法response就可以了,具体由谁处理女性提出的请求,都已经转移到了Handler抽象类中。

① 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(IWomen women) {
        if (women.getType() == this.level) {
            //一个女性要求逛街,你要处理这个请求
            this.response(women);
        } else {
            //有后续环节,才把请求往后递送
            if (this.nextHandler != null) { 
                this.nextHandler.HandleMessage(women);
            } else {
                //已经没有后续处理人了,不用处理了
                System.out.println("---没地方请示了,按不同意处理---\n");
            }
        }
    }

    /*
     * 如果不属于你处理的请求,你应该让她找下一个环节的人,如女儿出嫁了,
     * 还向父亲请示是否可以逛街,那父亲就应该告诉女儿,应该找丈夫请示
     */
    public void setNext(Handler _handler) {
        this.nextHandler = _handler;
    }

    //有请示那当然要回应
    protected abstract void response(IWomen women);
}

其实在这里也用到模板方法模式,在模板方法中判断请求的级别和当前能够处理的级别,如果相同则调用基本方法,做出反馈;如果不相等,则传递到下一个环节,由下一环节做出回应,如果已经达到环节结尾,则直接做不同意处理。基本方法response需要各个实现类实现,每个实现类只要实现两个职责:一是定义自己能够处理的等级级别;二是对请求做出回应。

② Father 子类

public class Father extends Handler {
    //父亲只处理女儿的请求
    public Father() {
        super(Handler.FATHER_LEVEL_REQUEST);
    }

    //父亲的答复
    protected void response(IWomen women) {
        System.out.println("--------女儿向父亲请示-------");
        System.out.println(women.getRequest());
        System.out.println("父亲的答复是:同意\n");
    }
}

③ Husband 子类

public class Husband extends Handler {
    //丈夫只处理妻子的请求
    public Husband() {
        super(Handler.HUSBAND_LEVEL_REQUEST);
    }

    //丈夫请示的答复
    protected void response(IWomen women) {
        System.out.println("--------妻子向丈夫请示-------");
        System.out.println(women.getRequest());
        System.out.println("丈夫的答复是:同意\n");
    }
}

④ Son 子类

public class Son extends Handler {
    //儿子只处理母亲的请求
    public Son() {
        super(Handler.SON_LEVEL_REQUEST);
    }

    //儿子的答复
    protected void response(IWomen women) {
        System.out.println("--------母亲向儿子请示-------");
        System.out.println(women.getRequest());
        System.out.println("儿子的答复是:同意\n");
    }
}

这三个类都很简单,构造方法是必须实现的,父类框定子类必须有一个显式构造函数,子类不实现编译不通过。通过构造方法我们设置了各个类能处理的请求类型,Father只能处理请求类型为1(也就是女儿)的请求;Husband只能处理请求类型类为2(也就是妻子)的请求,儿子只能处理请求类型为3(也就是母亲)的请求,那如果请求类型为4的该如何处理呢?在Handler中我们已经判断了,如何没有相应的处理者(也就是没有下一环节),则视为不同意。

⑤ Women 实现类

public class Women implements IWomen {
    /*
     * 通过一个int类型的参数来描述妇女的个人状况
     * 1--未出嫁
     * 2--出嫁
     * 3--夫死
     */
    private int type = 0;
    
    //妇女的请示
    private String request = "";

    //构造函数传递过来请求
    public Women(int type , String request) {
        this.type = type;
        //为了便于显示,在这里做了点处理
        switch (this.type) {
            case 1:
                this.request = "女儿的请求是:" + request;
                break;
            case 2:
                this.request = "妻子的请求是:" + request;
                break;
            case 3:
                this.request = "母亲的请求是:" + request;
            default:
                
        }
    }

    //获得自己的状况
    public int getType() {
        return this.type;
    }

    //获得妇女的请求
    public String getRequest() {
        return this.request;
    }
}

⑥ Client 测试类

public class Client {
    public static void main(String[] args) {
        //随机挑选几个女性
        Random rand = new Random();
        ArrayList<IWomen> arrayList = new ArrayList();
        for (int i = 0; i < 5; i++) {
            arrayList.add(new Women(rand.nextInt(4), "我要出去逛街"));
        }
        //定义三个请示对象
        Handler father = new Father();
        Handler husband = new Husband();
        Handler son = new Son();
        //设置请示顺序
        father.setNext(husband);
        husband.setNext(son);
        for (IWomen women : arrayList) {
            father.HandleMessage(women);
        }
    }
}

在Client中设置请求的传递顺序,先向父亲请示,不是父亲应该解决的问题,则由父亲传递到丈夫类解决,若不是丈夫类解决的问题则传递到儿子类解决,最终的结果必然有一个返回。

3. 责任链模式的定义

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

责任链模式的重点是在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果:

在这里插入图片描述

① Handler 抽象类

public abstract class Handler {
    
    private Handler nextHandler;

    //每个处理者都必须对请求做出处理
    //被final修改的抽象方法不能被继承
    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);
}

② ConcreteHandler1 处理者子类

public class ConcreteHandler1 extends Handler {
    /**
     * 定义自己的处理逻辑
     */
    protected Response echo(Request request) {
        //完成处理逻辑
        return null;
    }

    /**
     * 设置自己的处理级别
     */
    protected Level getHandlerLevel() {
        //设置自己的处理级别
        return null;
    }
}

③ ConcreteHandler2 处理者子类

public class ConcreteHandler2 extends Handler {
    /**
     * 定义自己的处理逻辑
     */
    protected Response echo(Request request) {
        //完成处理逻辑
        return null;
    }

    /**
     * 定义自己的处理逻辑
     */
    protected Level getHandlerLevel() {
        //设置自己的处理级别
        return null;
    }
}

④ ConcreteHandler3 处理者子类

public class ConcreteHandler3 extends Handler {
    /**
     * 定义自己的处理逻辑
     */
    protected Response echo(Request request) {
        //完成处理逻辑
        return null;
    }

    /**
     * 设置自己的处理级别
     */
    protected Level getHandlerLevel() {
        //设置自己的处理级别
        return null;
    }
}

⑤ Client 测试类

public 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);
        //提交请求,返回结果
        Response response = handler1.handlerMessage(new Request());
    }
}

责任链在实际的项目中使用也是比较多的,我曾经做过这样一个项目,界面上有一个用户注册功能,注册用户分两种,一种是VIP用户,也就是在该单位办理过业务的,一种是普通用户,一个用户的注册要填写一堆信息,VIP用户只比普通用户多了一个输入项:VIP序列号。注册后还需要激活,VIP和普通用户的激活流程也是不同的,VIP是自动发送邮件到用户的邮箱中就算激活了,普通用户要发送短信才能激活,为什么呢?获得手机号码以后好发广告短信啊!项目组就采用了责任链模式,甭管从前台传递过来的是VIP用户信息还是普通用户信息,统一传递到一个处理入口,通过责任链来完成任务的处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring-day01 spring全家桶,如何学习框架,单元测试

Spring学习-概述 1. spring全家桶&#xff1a;spring &#xff0c; springmvc &#xff0c;spring boot , spring cloud spring: 出现是在2002左右&#xff0c;解决企业开发的难度。减轻对项目模块之间的管理&#xff0c; 类和类之间的管理&#xff0c; 帮助开发人员创建对象&a…

Linux:iptables和firewalld基础j解析

目录 1、四表五链概念&#xff1a; 2、数据报文流程 3、iptables 与 firewalld 区别 4、DROP 和 REJECT策略的区别&#xff1a; 5、iptables命令参数 6、iptables基本的命令使用 7.firewalld&#xff1a;基于CLI&#xff08;命令行界面&#xff09;和基于GUI&#xff08;图…

多线程wait()和notify()方法详解

多线程wait()和notify()方法详解 文章目录多线程wait()和notify()方法详解前言一、线程间等待与唤醒机制二、等待方法wait()三、唤醒方法notify()四、关于wait和notify内部等待问题&#xff08;重要&#xff09;五、完整代码&#xff08;仅供测试用&#xff09;六、wait和sleep…

docker实战学习2022版本(六)之Dockerfile整合微服务实战

需求&#xff1a;通过idea新建一个普通微服务模块&#xff1b;然后通过Dockerfile发布微服务部署到docker容器 step1&#xff1a;新建一个springboot项目&#xff0c;添加依赖 <dependencies><!--SpringBoot通用依赖模块--><dependency><groupId>org.…

Clickhouse与Doris的区别

Doris使用较为简单&#xff0c;join功能更强大&#xff0c;运维更简单&#xff0c;灵活的扩容缩容&#xff0c;分布式更强&#xff0c;支持事务和幂等性导数 Clickhouse性能更佳&#xff0c;导入性能和单表查询性能更好&#xff0c;同时可靠性更好&#xff0c;支持非常多的表引…

谈一谈AI对人工的取代

文章目录AI绘画现在达到了什么水平&#xff1f;易用性怎么样&#xff1f;**缘起&#xff1a;2015年 用文字画画****2021年 Dalle 与 开源社区的程序员们****openAI与它并不open的Dalle****AI开源社区****Dream by [wombo](https://www.zhihu.com/search?qwombo&search_sou…

内农大《嵌入式基础》实验二 C语言进阶和Makefile

一、 实验目的 利用多文件编程&#xff0c;掌握Linux环境下C程序的编辑、编译、运行等操作。掌握Makefile文件的编写、变量及隐式规则和模式规则的应用。掌握Linux环境下main函数的参数。掌握各类指针的应用。 二、 实验任务与要求 根据实验要求编写C语言程序&#xff1b;写…

LiteIDE主题定制教程【续】

摘要&#xff1a;本篇文章是LiteIDE主题定制教程的续作&#xff0c;之所以会有这篇续作&#xff0c;是因为在写完那篇文章之后&#xff0c;我在使用过程中陆续发现了一些问题&#xff0c;以及一些可以优化的地方&#xff0c;我将这些内容作为补充放到这篇文章里。所有更新都已同…

<Linux系统复习>文件系统的理解

一、本章重点 1、磁盘的物理结构 2、磁盘文件如何存储&#xff1f; 3、目录的理解 4、创建一个文件做了什么&#xff1f; 5、删除一个文件做了什么&#xff1f; 6、软连接 7、硬链接 01 磁盘的物理结构 磁盘是硬件结构唯一的机械设备&#xff0c;它通过磁头来进行磁盘的读写&am…

LabVIEW前面板上的字体大小取决于操作系统

LabVIEW前面板上的字体大小取决于操作系统 创建了一个VI&#xff0c;其前面板使用了多个标签和文本。我发现Windows 7系统上的字体大小与Windows 10系统上的字体大小不同。这导致我的前面板看起来不像我希望在计算机上看到的那模样。如何使字体在所有Windows操作系统上变得相同…

【Linux_】权限

【Linux_】权限 心有所向&#xff0c;日复一日&#xff0c;必有精进专栏&#xff1a;《Linux_》作者&#xff1a;沂沐沐目录 【Linux_】权限 前言 Linux权限的概念&#xff08;是什么&#xff09;&#xff1f; 什么是权限&#xff1f; Linux权限管理 文件访问者的分类&am…

npm包学习

想开发自己的的工具包&#xff0c;那必然要借鉴一些常用的npm包来帮我们解决一些问题&#xff0c;下面就罗列一些在学习vue-cli实现原理时候遇到的一些依赖包吧。 1、chalk 用途&#xff1a;可以修改终端输出字符的颜色&#xff0c;类似css的color属性&#xff0c;npm地址&am…

100天精通Python(数据分析篇)——第62天:pandas常用统计方法与案例

文章目录每篇前言一、常用统计方法与案例1. 求和&#xff08;sum&#xff09;2. 求平均值&#xff08;mean&#xff09;3. 求最小值&#xff08;min&#xff09;4. 求最大值&#xff08;max&#xff09;5. 求中位数&#xff08;median&#xff09;6. 求众数&#xff08;mode&am…

jQuery网页开发案例:jQuery 其他方法--jQuery 拷贝对象,多库共存,jQuery 插件

jQuery 对象拷贝 如果想要把某个对象拷贝&#xff08;合并&#xff09; 给另外一个对象使用&#xff0c;此时可以使用 $.extend() 方法 语法&#xff1a; $.extend([deep], target, object1, [objectN]) 1. deep: 如果设为true 为深拷贝&#xff0c; 默认为false 浅拷贝 …

做减法才是真本事,别以为你很能学,做加法一点都不难

文章目录 顶级的高手才敢做减法 前言 一、做减法才是真本事 二、大数据梦想联盟活动开启 顶级的高手才敢做减法 前言 大多数人不懂&#xff0c;不会&#xff0c;不做&#xff0c;才是你的机会&#xff0c;你得行动&#xff0c;不能畏首畏尾 大数据等于趋势&#xff0c;一…

Vue中computed和watch区别

前言 vue中的computed和watch我们经常会用到&#xff0c;那么在什么场景下使用computed和watch&#xff0c;两者又有什么区别呢&#xff0c;傻傻分不清楚。记录一下&#xff0c;温故而知新&#xff01; computed computed是计算属性&#xff0c;基于data中声明过或者父组件传递…

makkefile文件自动化编译以及基础文件命令(补)

目录makefile文件&#xff1a;实现自动化编译基础文件命令find&#xff08;查找&#xff09;grep&#xff08;过滤&#xff09;| &#xff08;管道&#xff09;关机重启文件压缩解压分步压缩解压一步压缩解压makefile文件&#xff1a;实现自动化编译 文件名称必须是:makefile …

【day15】每日强训编程题——查找输入整数二进制中1的个数手套

查找输入整数二进制中1的个数_牛客题霸_牛客网 这道题非常简单&#xff0c;就一个思路&#xff1a; 按位与& 任何一个数按位与上1&#xff0c;如果这个数二进制的最后一位是1&#xff0c;那么按位与的结果就是1&#xff0c;否则就是0 代码思路&#xff1a;n按位与1后往右…

【splishsplash】PBD探究

上次我们探究了PBD是如何引入plishsplash的&#xff0c;以及其控制流。 https://blog.csdn.net/weixin_43940314/article/details/127569870 这次我们来讲如何在自己新建的类中控制PBD刚体。 上回说到 Simulator\PositionBasedDynamicsWrapper\PBDWrapper.cpp 中的 void PBD…

AXI协议详解(6)-原子访问

原子访问 本章介绍了 AXI 协议如何实现排他访问和锁定访问机制。 它包含以下部分&#xff1a; 原子访问排他访问锁定访问 6.1 原子访问 为了实现原子访问权限&#xff0c;ARLOCK[1:0] 或 AWLOCK[1:0] 信号提供排他访问和锁定访问。 表 6-1 显示了 ARLOCK[1:0] 和 AWLOCK[1:…