设计模式->观察者设计模式和订阅者发布者设计模式的区别

news2024/12/26 14:17:58

设计模式->观察者设计模式和订阅者发布者设计模式的区别

  • 一、先复习一下观察者设计模式的相关定义,优点,以及缺点
    • 1.定义
      • 观察者模式的三个典型例子
    • 2.优点
    • 3.缺点
    • 4.观察者设计模式的主要角色
    • 5.代码举例
      • 完整代码
  • 二、回答问题:观察者设计模式和订阅者发布者设计模式的区别

一、先复习一下观察者设计模式的相关定义,优点,以及缺点

1.定义

观察者模式是指多个对象存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象的状态都会得到通知并自动更新。
这种模式又叫订阅者/发布者模式(但是又与观察者略有不同,写在后面了)、模式-视图模式,他是 对象行为型模式。

观察者模式的三个典型例子

1.例子 一:
大家在学校上自习的时候,等老师走了有些人会玩手机、吃零食、交头接耳找隔壁妹妹聊天,但是被老师发现可就不好了,所以大家想了一个招:让坐在最后排的同学帮忙“放风”,老师一来就给大家一个手势通知大家,大家就继续装好好学生(放入北影教程)
这其实就是一个典型的观察者模式:
“放风”的同学是被观察者(目标)
玩手机、吃零食的同学是观察者,大家都在观察“放风”同学(目标)的手势,一旦老师来了,被观察者(目标)就会通知大家。
2.例子 二:
csdn博客订阅就是采用观察者模式:
当博主发表新文章的时候,即博主的状态改变了变化,那些订阅的读者就会收到通知,然后进行相应的动作,比如去查看文章或者一键三连。博主与读者之间就会存在一对多的依赖关系。
3.例子 三:
事件触发模型

2.优点

1) 降低了目标与观察者之前的耦合关系。
在观察者模式中,观察者对象和主题对象之间是直接的关联关系,但是主题对象并不知道观察者对象的具体实现,而是只知道它们实现了共同的接口或抽象类。这样,主题对象和观察者对象之间的依赖关系就非常松散,它们可以独立地进行修改和扩展,而不会对彼此产生太大的影响。

2)目标与观察者之前建立了一套触发机制。

3.缺点

1)目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
解释:如果目标对象的实现发生变化,可能会影响到观察者对象的实现。这种依赖关系虽然比较松散,但并没有完全解除。
由于观察者对象需要订阅目标对象的状态变化事件,因此它们之间可能会形成循环引用。例如,当一个目标对象的状态发生变化时,它需要通知所有的观察者对象,而某些观察者对象可能需要调用目标对象的方法来获取最新的状态信息。如果这些观察者对象持有目标对象的引用,就可能出现循环引用的情况,从而导致内存泄漏等问题。

所以,为了解决以上问题,订阅者/发布者模式应运而生,订阅者/发布者模式使用了事件通道为目标和观察者搭建一个桥梁。订阅者/发布者模式(Publish-Subscribe Pattern)的一个重要特点就是使用了事件通道(Event Channel)来解除发布者(Publisher)和订阅者(Subscriber)之间的直接依赖关系,从而避免了循环引用等问题。

2)当观察者对象很多时,目标通知观察者就会花费很多时间,影响程序的效率。

4.观察者设计模式的主要角色

1)抽象主题(Subject)角色:【基类】
也叫抽象目标类,他提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。

2)具体主题(Concrete Subject)角色:

3)抽象观察者(Observer)角色:【基类】

4)具体观察者(Concrete Observer)角色:

5.代码举例

// 抽象观察者(基类)
class Observer
{
public:
    Observer() {}
    virtual ~Observer() {}
    virtual void Update() {}
};
// 抽象博客(基类)
class Blog
{
public:
    Blog() {}
    virtual ~Blog() {}
    void Attach(Observer *observer) { m_observers.push_back(observer); } // 添加观察者
    void Remove(Observer *observer) { m_observers.remove(observer); }    // 移除观察者
    void Notify()                                                        // 通知观察者
    {
        list<Observer *>::iterator iter = m_observers.begin();//遍历观察者链表
        for (; iter != m_observers.end(); iter++)
            (*iter)->Update();
    }
    virtual void SetStatus(string s) { m_status = s; } // 设置状态
    virtual string GetStatus() { return m_status; }    // 获得状态
private:
    list<Observer *> m_observers; // 观察者链表
protected:
    string m_status; // 状态
};

以上是观察者和博客(目标/被观察者/发布者),定义了通用接口。
博客类主要完成了观察者的添加、移除、通知操作。

// 具体博客类
class BlogCSDN : public Blog
{
private:
    string m_name; // 博主名称
public:
    BlogCSDN(string name) : m_name(name) {}
    ~BlogCSDN() {}
    void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } // 具体设置状态信息
    string GetStatus() { return m_status; }
};
// 具体观察者
class ObserverBlog : public Observer
{
private:
    string m_name; // 观察者名称
    Blog *m_blog;  // 观察的博客,当然以链表形式更好,就可以观察多个博客
public:
    ObserverBlog(string name, Blog *blog) : m_name(name), m_blog(blog) {}
    ~ObserverBlog() {}
    void Update() // 获得更新状态
    {
        string status = m_blog->GetStatus();
        cout << m_name << "-------" << status << endl;
    }
};
// 测试案例
int main()
{
    Blog *blog = new BlogCSDN("萧炎");
    Observer *observer1 = new ObserverBlog("观察者:冰皇海波东", blog);
    blog->Attach(observer1);
    blog->SetStatus("发表博客<<设计模式C++实现15——观察者模式>>");
    blog->Notify();
    delete blog;
    delete observer1;
    return 0;
}

完整代码

#include <iostream>
#include <list>

using namespace std;

// 抽象观察者(基类)
class Observer
{
public:
    Observer() {}
    virtual ~Observer() {}
    virtual void Update() {}
};
// 抽象博客(基类)
class Blog
{
public:
    Blog() {}
    virtual ~Blog() {}
    void Attach(Observer *observer) { m_observers.push_back(observer); } // 添加观察者
    void Remove(Observer *observer) { m_observers.remove(observer); }    // 移除观察者
    void Notify()                                                        // 通知观察者
    {
        list<Observer *>::iterator iter = m_observers.begin();
        for (; iter != m_observers.end(); iter++)
            (*iter)->Update();
    }
    virtual void SetStatus(string s) { m_status = s; } // 设置状态
    virtual string GetStatus() { return m_status; }    // 获得状态
private:
    list<Observer *> m_observers; // 观察者链表
protected:
    string m_status; // 状态
};

// 具体博客类
class BlogCSDN : public Blog
{
private:
    string m_name; // 博主名称
public:
    BlogCSDN(string name) : m_name(name) {}
    ~BlogCSDN() {}
    void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } // 具体设置状态信息
    string GetStatus() { return m_status; }
};
// 具体观察者
class ObserverBlog : public Observer
{
private:
    string m_name; // 观察者名称
    Blog *m_blog;  // 观察的博客,当然以链表形式更好,就可以观察多个博客
public:
    ObserverBlog(string name, Blog *blog) : m_name(name), m_blog(blog) {}
    ~ObserverBlog() {}
    void Update() // 获得更新状态
    {
        string status = m_blog->GetStatus();
        cout << m_name << "-------" << status << endl;
    }
};

// 测试案例
int main()
{
    Blog *blog = new BlogCSDN("萧炎");
    Observer *observer1 = new ObserverBlog("观察者:冰皇海波东", blog);
    blog->Attach(observer1);
    blog->SetStatus("发表博客<<设计模式C++实现15——观察者模式>>");
    blog->Notify();
    delete blog;
    delete observer1;
    return 0;
}

//观察者:冰皇海波东-------CSDN通知 : 萧炎发表博客<<设计模式C++实现15——观察者模式>>

二、回答问题:观察者设计模式和订阅者发布者设计模式的区别

这两种模式之间的主要区别可以参考下图:
在这里插入图片描述
可以这么回答:

  1. 在23种基本的设计模式并没有订阅-发布模式,如果非要说有什么区别的话,我认为,订阅者发布者设计模式是观察者模式的变体,它们大部分是一致的,有很多相似之处,但并不完全相同,具体的区别有如下几点:

  2. 观察者模式里只有两个角色:观察者和被观察者。
    而,对于订阅发布者模式而言,它则有三种角色:发布者、订阅者、调度器(第三者->事件通道(Event Channel))。

  3. 观察者(Observer)模式中,观察者(Observers)角色知道抽象主题(Subject)角色,同时 , 抽象主题(Subject)角色 还保留了 观察者(Observers)角色 的记录。
    然而,在发布者/订阅者中,发布者和订阅者不需要彼此了解。他们只是在消息队列或代理的帮助下进行通信。
    订阅者/发布者(Subscriber / Publisher)模式中,组件是松散耦合(即彼此之间的依赖关系比较少,它们可以独立地进行修改和扩展,而不会对彼此产生太大的影响。)的;相比Observer模式,订阅者发布者模式的耦合更低。

  4. 观察者模式主要以同步方式实现,即当某些事件发生时,Subject调用其所有观察者的适当方法。发布者/订阅者在大多情况下是异步方式(使用消息队列)

  5. 观察者模式需要在单个应用程序地址空间中实现。而订阅者/发布者模式更像是跨应用程序模式。

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

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

相关文章

【Java-SpringBoot+Vue+MySql】项目开发综合—经验总结

目录 框架&#xff1a; 编程思维&#xff1a; MVC架构: 前端——组件式开发 开发思路梳理&#xff1a; 后端—— 前端—— 效果图 信息列表&#xff1a; 修改用户​编辑 新增用户 删除用户 数据清空 批量上传 框架&#xff1a; 后端&#xff1a;JAVA-SpringBoot2.6、包管理器M…

13.RocketMQ之消息的存储与发送

1. 消息存储 1.1 消息存储 分布式队列因为有高可靠性的要求&#xff0c;所以数据要进行持久化存储。 消息生成者发送消息Broker收到消息&#xff0c;将消息进行持久化&#xff0c;在存储中新增一条记录返回ACK给生产者Broker消息给对应的消费者&#xff0c;然后等待消费者返回A…

Keras-4-深度学习用于计算机视觉-猫狗数据集训练卷积网络

0. 说明&#xff1a; 本篇学习记录主要包括&#xff1a;《Python深度学习》的第5章&#xff08;深度学习用于计算机视觉&#xff09;的第2节&#xff08;在小型数据集上从头开始训练一个卷积神经网络&#xff09;内容。 相关知识点&#xff1a; 从头训练卷积网络&#xff1b…

AI 绘画用 Stable Diffusion 图生图局部重绘功能给美女换装(这是我能看的嘛)

昨天带大家一起装好了 Stable Diffusion 的环境&#xff0c;今天就来带大家一起体验一下 Stable Diffusion 的局部重绘功能。 没装好环境的可以看上一篇&#xff1a;AI 绘画基于 Kaggle 10 分钟搭建 Stable Diffusion&#xff08;保姆级教程&#xff09; Stable Diffusion 的…

可重入,可打断,公平锁,条件变量原理解读

目录 可重入原理 可打断原理 不可打断模式 可打断模式 公平锁实现原理 条件变量实现原理 await 流程 signal 流程 可重入原理 什么是可重入&#xff1a;当线程请求一个由其它线程持有的对象锁时&#xff0c;该线程会阻塞&#xff0c;而当线程请求由自己持有的对象锁…

阿里刚换帅,京东忙换将:新时代号角吹响

6月26日早间&#xff0c;京东物流在港交所发布公告称&#xff0c;京东物流CEO余睿因个人身体原因辞任执行董事、首席执行官及授权代表&#xff0c;原京东产发CEO胡伟将担任京东物流CEO。 同时&#xff0c;据《科创板日报》报道&#xff0c;京东集团将新成立创新零售部&#xf…

【论文笔记】Fast Segment Anything

我说个数&#xff1a;一个月5篇基于Fast Segment Anything的改进的论文就会出现哈哈哈哈。 1.介绍 1.1 挑战 SAM架构的主要部分Transformer&#xff08;ViT&#xff09;模型相关的大量计算资源需求&#xff0c;这给其实际部署带来了障碍 1.2 任务解耦 将分段任意任务解耦为…

正确认识:1189194-65-7,DOTA-CH2-Alkynyl (TFA salt),试剂的结构式和CAS

文章关键词&#xff1a;双功能螯合剂&#xff0c;大环配体&#xff0c;标记螯合剂修饰 【产品描述】 DOTA-CH2-Alkynyl (TFA salt)中TFA是一种强酸。它可以质子化任何氨基。盐酸也是这样。在纯化多肽过程中的反相HPLC&#xff0c;有一种技术是阴离子交换。将多肽加载在柱子上&a…

MySql基础教程(三):创建数据表、数据增删改查、删除数据表

MySql基础教程(三)&#xff1a;创建数据表、数据增删改查、删除数据表 1、创建数据表 创建MySQL数据表需要以下信息&#xff1a; 表名表字段名定义每个表字段 1.1 语法 下面是创建MySQL数据表的SQL通用语法&#xff1a; CREATE TABLE table_name (column_name column_typ…

无线蓝牙通信有关(NRF2401模块)的功耗,通道频率等

参考&#xff1a; ISM频段 Industrial Scientific Medical,ISM&#xff08;工业、科学、医疗&#xff09;频段为国际电信联盟&#xff08;ITU&#xff09;《无线电规则》定义的指定无线电频段。 Frequency-Shift Keying 数字调制技术&#xff08;FSK调制&#xff09; 将需要…

又是一年毕业季,准备好踏入职场了吗?

文章目录 一、大学时光二、给毕业生的一些建议三、职场中的经验分享四、程序员未来职业规划 一、大学时光 作为一名程序员&#xff0c;大学时光是我职业生涯中最重要的时期之一。这四年的大学&#xff0c;我不仅学到了计算机科学的理论知识&#xff0c;还积累了丰富的编程经验…

tqdm:python的简单可视化进度

tqdm&#xff1a;python的简单可视化进度 说明 ​ 本篇文章的主要目的是快速上手使用&#xff0c;而不是解析源码。 目录结构 文章目录 tqdm&#xff1a;python的简单可视化进度1. 应用场景2. 库安装3. 方法速览4. 案例5. 总结 1. 应用场景 ​ 进度条应用的场景很多&#xff0…

YOLOv8独家原创改进:独家首发最新原创XIoU_NMS改进点,改进有效可以直接当做自己的原创改进点来写,提升网络模型性能、收敛速度和鲁棒性

💡该教程为属于《芒果书》📚系列,包含大量的原创首发改进方式, 所有文章都是全网首发原创改进内容🚀 💡本篇文章为YOLOv8独家原创改进:独家首发最新原创XIoU_NMS改进点,改进有效可以直接当做自己的原创改进点来写,提升网络模型性能、收敛速度和鲁棒性。 💡对自己…

[RocketMQ] Producer发送消息的总体流程 (七)

单向发送: 把消息发向Broker服务器, 不管Broker是否接收, 只管发, 不管结果。同步发送: 把消息发向Broker服务器, 如果Broker成功接收, 可以得到Broker的响应。异步发送: 把消息发向Broker服务器, 如果Broker成功接收, 可以得到Broker的响应。异步所以发送消息后, 不用等待, 等…

css基础知识十:介绍一下CSS中的Grid网格布局?

一、是什么 Grid 布局即网格布局&#xff0c;是一个二维的布局方式&#xff0c;由纵横相交的两组网格线形成的框架性布局结构&#xff0c;能够同时处理行与列 擅长将一个页面划分为几个主要区域&#xff0c;以及定义这些区域的大小、位置、层次等关系 这与之前讲到的flex一维…

操作系统—内存管理

单片机是没有操作系统的&#xff0c;每次写完代码都是通过一些工具将程序直接烧录进去&#xff0c;这样程序才能跑起来。单片机的CPU是直接操作内存的物理地址。在这种情况下&#xff0c;要想在内存中同时运行两个程序是不可能的&#xff0c;程序会崩溃。那么操作系统为了解决这…

LLM相关的一些调研

Prompt Engine 可以参考该项目&#xff0c;该项目提供关于提示词书写的规则。由openai以及吴恩达完成。 https://github.com/datawhalechina/prompt-engineering-for-developers由于目前chatgpt 无法直接在国内访问&#xff0c;推荐在claude on slack上尝试。关于claude api h…

Leetcode:1035. 不相交的线、53. 最大子数组和(C++)

目录 1035. 不相交的线 题目描述&#xff1a; 实现代码与解析&#xff1a; 动态规划 原理解析&#xff1a; 53. 最大子数组和 题目描述&#xff1a; 实现代码与解析&#xff1a; 动态规划 原理思路&#xff1a; 1035. 不相交的线 题目描述&#xff1a; 在两条独立的水…

移动端永不过时的高薪技术岗位,原来是它……

随着 Android 设备的普及和应用领域的不断扩大&#xff0c;Android Framework 开发需求量将会持续增长&#xff0c;并且会越来越多地向行业、企业级应用和系统优化等方向发展。以下是一些 Android Framework 开发相关的应用场景&#xff1a; 1. 特定垂直领域的智能设备&#x…

Jmeter性能测试

一、jmeter多并发 1.线程设置&#xff1a; 线程数——多少个虚拟用户 ramp_up时间(秒)——时间&#xff0c;设置时间内将线程都跑完 循环次数——勾选永远&#xff0c;就一直跑&#xff0c;直到手动停止&#xff1b;输入数字&#xff0c;就是循环多少次 2.jmeter逻辑分支控制…