责任链模式和观察者模式

news2025/1/11 16:01:12

1、责任链模式

1.1 概述

        在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花”游戏等。

定义:

        又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

1.2 结构

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

1.3 案例实现

        现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。

类图如下:

 代码如下:

//请假条
public class LeaveRequest {
    private String name;//姓名
    private int num;//请假天数
    private String content;//请假内容

    public LeaveRequest(String name, int num, String content) {
        this.name = name;
        this.num = num;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public int getNum() {
        return num;
    }

    public String getContent() {
        return content;
    }
}

//处理者抽象类
public abstract class Handler {
    protected final static int NUM_ONE = 1;
    protected final static int NUM_THREE = 3;
    protected final static int NUM_SEVEN = 7;

    //该领导处理的请假天数区间
    private int numStart;
    private int numEnd;

    //领导上面还有领导
    private Handler nextHandler;

    //设置请假天数范围 上不封顶
    public Handler(int numStart) {
        this.numStart = numStart;
    }

    //设置请假天数范围
    public Handler(int numStart, int numEnd) {
        this.numStart = numStart;
        this.numEnd = numEnd;
    }

    //设置上级领导
    public void setNextHandler(Handler nextHandler){
        this.nextHandler = nextHandler;
    }

    //提交请假条
    public final void submit(LeaveRequest leave){
        if(0 == this.numStart){
            return;
        }

        //如果请假天数达到该领导者的处理要求
        if(leave.getNum() >= this.numStart){
            this.handleLeave(leave);

            //如果还有上级 并且请假天数超过了当前领导的处理范围
            if(null != this.nextHandler && leave.getNum() > numEnd){
                this.nextHandler.submit(leave);//继续提交
            } else {
                System.out.println("流程结束");
            }
        }
    }

    //各级领导处理请假条方法
    protected abstract void handleLeave(LeaveRequest leave);
}

//小组长
public class GroupLeader extends Handler {
    public GroupLeader() {
        //小组长处理1-3天的请假
        super(Handler.NUM_ONE, Handler.NUM_THREE);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("小组长审批:同意。");
    }
}

//部门经理
public class Manager extends Handler {
    public Manager() {
        //部门经理处理3-7天的请假
        super(Handler.NUM_THREE, Handler.NUM_SEVEN);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("部门经理审批:同意。");
    }
}

//总经理
public class GeneralManager extends Handler {
    public GeneralManager() {
        //部门经理处理7天以上的请假
        super(Handler.NUM_SEVEN);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("总经理审批:同意。");
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        //请假条来一张
        LeaveRequest leave = new LeaveRequest("小花",5,"身体不适");

        //各位领导
        GroupLeader groupLeader = new GroupLeader();
        Manager manager = new Manager();
        GeneralManager generalManager = new GeneralManager();

        groupLeader.setNextHandler(manager);//小组长的领导是部门经理
        manager.setNextHandler(generalManager);//部门经理的领导是总经理
        //之所以在这里设置上级领导,是因为可以根据实际需求来更改设置,如果实战中上级领导人都是固定的,则可以移到领导实现类中。

        //提交申请
        groupLeader.submit(leave);
    }
}

1.4 优缺点

1 优点:

  • 降低了对象之间的耦合度

    该模式降低了请求发送者和接收者的耦合度。

  • 增强了系统的可扩展性

    可以根据需要增加新的请求处理类,满足开闭原则。

  • 增强了给对象指派职责的灵活性

    当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。

  • 责任链简化了对象之间的连接

    一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

  • 责任分担

    每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

2 缺点:

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。

  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。

  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

2、观察者模式

2.1 概述        

        又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

2.2 结构

在观察者模式中有如下角色:

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

2.3 案例实现

【例】微信公众号

        在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号。

类图如下:

 

代码如下:

定义抽象观察者类,里面定义一个更新的方法

public interface Observer {
    void update(String message);
}

 定义具体观察者类,微信用户是观察者,里面实现了更新的方法

public class WeixinUser implements Observer {
    // 微信用户名
    private String name;

    public WeixinUser(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println(name + "-" + message);
    }
}

 定义抽象主题类,提供了attach、detach、notify三个方法

public interface Subject {
    //增加订阅者
    public void attach(Observer observer);

    //删除订阅者
    public void detach(Observer observer);
    
    //通知订阅者更新消息
    public void notify(String message);
}

 微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法

public class SubscriptionSubject implements Subject {
    //储存订阅公众号的微信用户
    private List<Observer> weixinUserlist = new ArrayList<Observer>();

    @Override
    public void attach(Observer observer) {
        weixinUserlist.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        weixinUserlist.remove(observer);
    }

    @Override
    public void notify(String message) {
        for (Observer observer : weixinUserlist) {
            observer.update(message);
        }
    }
}

 客户端程序

public class Client {
    public static void main(String[] args) {
        SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
        //创建微信用户
        WeixinUser user1=new WeixinUser("孙悟空");
        WeixinUser user2=new WeixinUser("猪悟能");
        WeixinUser user3=new WeixinUser("沙悟净");
        //订阅公众号
        mSubscriptionSubject.attach(user1);
        mSubscriptionSubject.attach(user2);
        mSubscriptionSubject.attach(user3);
        //公众号更新发出消息给订阅的微信用户
        mSubscriptionSubject.notify("传智黑马的专栏更新了");
    }
}

2.4 优缺点

优点:

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。

  • 被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】

缺点:

  • 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时

  • 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃

2.5 使用场景

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时。

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

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

相关文章

【微积分听课笔记】全微分,二元极值,Double Integral

6.6 二元函数的极值_哔哩哔哩_bilibili 此笔记为听课笔记&#xff0c;宋浩老师微积分~ 最近诸事缠身&#xff0c;会有种会不会只做一件事好些。实际上&#xff0c;关键在于动力&#xff0c;我不可能每次都准备充分。动力&#xff0c;分配&#xff0c;这是目前进入大学我正在学…

用户至上!探索7种常用的用户体验研究方法

用户体验研究是产品开放过程中的重要组成部分&#xff0c;优秀的产品设计与高质量的用户体验是不可分割的。对于产品开发&#xff0c;选择合适的用户体验研究方法在很大程度上决定了产品的使用效果。本文全面阐述了用户体验研究、用户体验研究的重要性和用户体验研究方法&#…

138.随机链表的复制

/*** Definition for a Node.* struct Node {* int val;* struct Node *next;* struct Node *random;* };*/ typedef struct Node Node; struct Node* copyRandomList(struct Node* head) {Node* curhead;//拷贝节点插入到原节点后面while(cur){Node* copy(Node*)m…

产业项目招商活动会议课程报名签到h5小程序pc开源版开发

产业项目招商活动会议课程报名签到h5小程序pc开源版开发 一个集PC和移动端功能于一体的解决方案&#xff0c;线上线下进行服务&#xff0c;围绕 活动报名、在线课程、项目大厅、线下签到、会员系统等。为商会提供了更加便捷高效的管理方式&#xff0c;提升了商会活动和项目的组…

IaC实战指南:DevOps的自动化基石

基础设施即代码&#xff08;Infrastructure as Code&#xff0c;IaC&#xff09;是指利用脚本、配置或编程语言创建和维护基础设施的一组实践和流程。通过IaC&#xff0c;我们可以轻松测试各个组件、实现所需的功能并在最小化停机时间的前提下进行扩展。更值得一提的是&#xf…

学习通下载PDF资源

今天突然发现&#xff0c;学习通的pdf资源居然是没有下载入口的&#xff0c;这整的我想cv一下我的作业都搞不了&#xff0c;于是我一怒之下&#xff0c;怒了一下。 可以看到学习通的pdf资源是内嵌在网页的&#xff0c;阅读起来很不方便&#xff0c;虽然他内置了阅读器&#xf…

游泳耳机哪个牌子好性价比高?优选四大品质卓越品牌,诚挚推荐

随着科技的日新月异&#xff0c;游泳与音乐的融合已不再是遥不可及的梦想&#xff0c;一款性价比高的游泳耳机成为了许多游泳爱好者提升运动体验、激发运动热情的必备工具。可面对市场上种类繁多、价格各异的游泳耳机品牌&#xff0c;如何在确保高品质的同时&#xff0c;寻觅到…

使用antiSMASH数据库及软件分析微生物组

Introduction 上次简要介绍过了微生物组中生物合成基因簇&#xff08;BGCs&#xff09;分析&#xff0c;这次具体讲解使用antiSMASH数据库及软件分析的流程。 antiSMASH&#xff08;antibiotics & Secondary Metabolite Analysis Shell&#xff09;是一个用于识别和分析微…

解决“您的连接不是私密连接”

目录 那么为什么会出现这样提示呢 https访问有什么不同 将http访问更改为https访问 当您在浏览网页时&#xff0c;遇到“您的连接不是私密连接”的提示&#xff0c;这通常表示浏览器认为您的连接不够安全。这是因为浏览器无法信任网站使用的SSL证书&#xff0c;或者网站没有…

分布式光纤测温DTS的测温范围是多少?

分布式光纤测温DTS的测温范围不仅仅取决于光缆的感温能力&#xff0c;还受到多种复杂因素的影响。尽管高温光缆可以耐高温&#xff0c;低温光缆可以耐低温&#xff0c;甚至镀金光缆能够耐受高达700摄氏度的极高温度&#xff0c;然而&#xff0c;这些因素并不能完全解释测温范围…

【源码】[第二版]亲测完美双端获取通讯录、相册、短信定位源码

这套跟前面发的那套差不多&#xff0c;UI不一样而已。这套带了配套视频搭建教程&#xff0c;省的有些人问怎么搭建。 这套只能用HB打包&#xff0c;别问能不能获取苹果&#xff0c;ios短信谁也获取不了&#xff0c;ios相册的话自己研究下HB开权限。 还是推荐这套直接反编译改…

重大消息!软考高级论文单考,综合和案例连考

依据辽宁省信息技术教育中心&#xff08;辽宁省软考办&#xff09;发布《关于2024年上半年计算机技术与软件专业技术资格(水平)考试批次安排的通知》可知&#xff0c;2024年上半年软考有如下调整&#xff1a; 1.软考高级考试中&#xff0c;综合知识和案例分析连考&#xff08;…

性能测试 | 性能工具你用对了吗?

导读 要对客户端应用程序进行性能测试&#xff0c;需要了解用户场景和性能目标&#xff0c;选择合适的工具或方法来衡量和改进性能。其中涉及的性能指标有很多&#xff0c;如cpu使用率、内存、磁盘IO&#xff0c;相对应的性能观察工具也层出不穷&#xff0c;面对这些工具我们该…

24_Scala集合Map

文章目录 Scala集合Map1.构建Map2.增删改查3.Map的get操作细节 Scala集合Map –默认immutable –概念和Java一致 1.构建Map –创建kv键值对 && kv键值对的表达 –创建immutable map –创建mutable map //1.1 构建一个kv键值对 val kv "a" -> 1 print…

嵌入式Linux开发如何查看应用所链接的动态库

在开发中我们常常需要查看一个应用究竟链接了哪些对应的动态库 桌面linux的使用方法不赘述&#xff0c;网上资料有很多&#xff0c;对于嵌入式linux开发中&#xff0c;我们在ubuntu中使用ldd 是不行的 应该使用

全面升级企业网络安全 迈入SASE新时代

随着数字化业务、云计算、物联网和人工智能等技术的飞速发展&#xff0c;企业的业务部署环境日渐多样化&#xff0c;企业数据的存储由传统的数据中心向云端和SaaS迁移。远程移动设备办公模式的普及&#xff0c;企业多分支机构的加速设立&#xff0c;也使得企业业务系统的用户范…

数据结构-线性表-应用题-2.2-14

1&#xff09;算法基本设计思想&#xff1a; 2&#xff09;c语言描述&#xff1a; #define INT_MAX 0X7FFFFFFF int abs_(int a) {//绝对值if(a<0) return -a;else return a; } bool min(int a,int b,int c){if(a<b&&a<c) return true;else return false; } …

【Kali Linux工具篇】使用Aircrack-ng破解wifi密码

前期准备 1、一个无线网卡 2、密钥爆破字典 实站过程 1、设置wlan为监听模式 airmon-ng start wlan0 #开启网卡wlan0监听模式 iwconfig #查看网卡状态&#xff0c;发现wlan0mon&#xff0c;表示已开启成功2、探测附近WiFi airdump-ng wlan0monBSSID 代表…

遥感+大数据为智慧无人农场按下“倍速键”

春回大地万象“耕”新&#xff0c;在襄阳市襄州区张家集镇近2000亩小麦绿意盎然、勃勃生机。 湖北绿神农业科技有限公司的生产经理王真指着监控室的电脑屏幕&#xff0c;告诉记者在与珈和科技合作开发的农田遥感监测平台上各类农田数据一目了然&#xff0c;为实现农业智能化管理…

await执行顺序

用一个简单的例子来说明&#xff0c;await到底在等待什么 求代码的打印顺序 function testAsy(x) {return new Promise((resolve) > {setTimeout(() > {resolve(x);}, 3000);}); } async function testAwt() {console.log("async开始执行");// 最先打印let r…