设计模式之观察者模式,以C++为例。

news2024/11/29 12:32:27

        今天来准备浅浅的过一下观察者模式,观察者模式也叫作:发布者订阅者模式。该模式的特点是多个对象依赖一个对象,为一对多依赖关系,每当一个对象改变时,所有依赖它的对象都会得到通知并自动更新,该模式主要维护主题类和观察者类。使用观察者模式的典型应用场景为:系统事件通知、电商订阅、消息订阅、状态更新、动态图形,这些场景多为事件驱动系统。它的实现同样以C++的继承、封装、多台为地基。

目录

一、观察者模式简单介绍

二、简单的观察者模式

三、多线程观察者模式

四、线程池实现

五、简单的电商订阅系统


代码链接:

Thomas_Lbw / Cpp_Design_Patterns · GitCode

一、观察者模式简单介绍

        观察者模式具体由以下几个模块组成:

        1. 抽象主题(Subject):它将所有观察者对象的引用保存在同一个集合里,每个主题都有任意数量的观察者。抽象主题提供外部接口用于增加、删除对象。

class Subject {
 public:
  virtual void RegisterObserver(Observer* observer) = 0;
  virtual void RemoveObserver(Observer* observer) = 0;
  virtual void NotifyObservers() = 0;
};

        2. 具体主题(Concrete Subject):将有关状态存入具体观察者对象。具体主题的内部状态改变,给所有登记过的观察者发出通知。

// 具体主题类
class ConcreteSubject : public Subject {
 public:
  void RegisterObserver(Observer* observer) override {
    observers_.push_back(observer);
  }

  void RemoveObserver(Observer* observer) override {
    auto it = std::find(observers_.begin(), observers_.end(), observer);
    if (it != observers_.end()) {
      observers_.erase(it);
    }
  }

  void NotifyObservers() override {
    for (Observer* observer : observers_) {
      observer->Update(value_);
    }
  }
  void SetValue(int value) {
    value_ = value;
    NotifyObservers();
  }

 private:
  std::vector<Observer*> observers_;
  int value_;
};

        3. 抽象观察者(Observer):为目标发生改变时需获得通知的对象定义一个更新接口。

// 抽象观察者类
class Observer {
 public:
  virtual void Update(int value) = 0;
};

        4. 具体观察者(Concrete Observer):存储有关状态,这些状态跟目标状态保持一致。提供Observer的更新接口以使自身状态与目标状态保持一致。 

class ConcreteObserver : public Observer {
 public:
  ConcreteObserver(int id) : id_(id) {}

  void Update(int value) override {
    std::cout << "Observer " << id_ << " received update: " << value << std::endl;
  }

 private:
  int id_;
};

小结:观察者模式定义了一种一对多的关系,即一个主题对象可以有多个观察者,当主题对象状态发生改变时,会通知所有的观察者。观察者模式基于对象间的松散耦合,通过解耦对象之间的依赖关系,从而使得系统更加灵活,易于维护和扩展。 

二、简单的观察者模式

        代码示例:

#include <iostream>
#include <vector>
#include <algorithm>

// 抽象观察者类
class Observer {
 public:
  virtual void Update(int value) = 0;
};

// 具体观察者类
class ConcreteObserver : public Observer {
 public:
  ConcreteObserver(int id) : id_(id) {}

  void Update(int value) override {
    std::cout << "Observer " << id_ << " received update: " << value << std::endl;
  }

 private:
  int id_;
};

// 抽象主题类
class Subject {
 public:
  virtual void RegisterObserver(Observer* observer) = 0;
  virtual void RemoveObserver(Observer* observer) = 0;
  virtual void NotifyObservers() = 0;
};

// 具体主题类
class ConcreteSubject : public Subject {
 public:
  void RegisterObserver(Observer* observer) override {
    observers_.push_back(observer);
  }

  void RemoveObserver(Observer* observer) override {
    auto it = std::find(observers_.begin(), observers_.end(), observer);
    if (it != observers_.end()) {
      observers_.erase(it);
    }
  }

  void NotifyObservers() override {
    for (Observer* observer : observers_) {
      observer->Update(value_);
    }
  }

  void SetValue(int value) {
    value_ = value;
    NotifyObservers();
  }

 private:
  std::vector<Observer*> observers_;
  int value_;
};

int main() {
  ConcreteSubject subject;
  ConcreteObserver observer1(1);
  ConcreteObserver observer2(2);

  subject.RegisterObserver(&observer1);
  subject.RegisterObserver(&observer2);

  subject.SetValue(123);

  subject.RemoveObserver(&observer1);

  subject.SetValue(456);

  return 0;
}

        执行结果:

        1. 抽象观察者类 Observer:提供了一个虚拟函数 Update,用于更新观察者对象的状态。

        2. 具体观察者类 ConcreteObserver:继承自 Observer,并实现了 Update 函数。在该类中,通过输出语句来模拟观察者更新自身状态的过程。

        3. 抽象主题类 Subject:提供了注册、删除和通知观察者的接口。

        4. 具体主题类 ConcreteSubject:继承自 Subject,并实现了注册、删除和通知观察者的接口。在该类中,通过维护一个存储观察者的容器来管理观察者的注册、删除和通知。

        5. main 函数:创建了一个 ConcreteSubject 和两个 ConcreteObserver 对象,通过注册、删除和通知等操作来演示观察者模式的基本逻辑。

类图:

 三、多线程观察者模式

        为保护对象安全,可以采用多线程来实现:

#include <iostream>
#include <vector>
#include <algorithm>
#include <thread>
#include <mutex>

class Observer {
public:
	virtual void Update(int value) = 0;
};

class ConcreteObserver : public Observer {
public:
	ConcreteObserver(int id) : id_(id) {}

	void Update(int value) override {
		std::cout << "Observer " << id_ << " received update: " << value << std::endl;
	}

private:
	int id_;
};

class Subject {
public:
	virtual void RegisterObserver(Observer* observer) = 0;
	virtual void RemoveObserver(Observer* observer) = 0;
	virtual void NotifyObservers() = 0;
};

class ConcreteSubject : public Subject {
public:
	void RegisterObserver(Observer* observer) override {
		std::unique_lock<std::mutex> lock(observers_mutex_);
		observers_.push_back(observer);
	}

	void RemoveObserver(Observer* observer) override {
		std::unique_lock<std::mutex> lock(observers_mutex_);
		auto it = std::find(observers_.begin(), observers_.end(), observer);
		if (it != observers_.end()) {
			observers_.erase(it);
		}
	}

	void NotifyObservers() override {
		std::unique_lock<std::mutex> lock(observers_mutex_);
		for (Observer* observer : observers_) {
			std::thread t(&Observer::Update, observer, value_);
			t.detach();
		}
	}

	void SetValue(int value) {
		value_ = value;
		NotifyObservers();
	}

private:
	std::vector<Observer*> observers_;
	std::mutex observers_mutex_;
	int value_;
};

int main() {
	ConcreteSubject subject;
	ConcreteObserver observer1(1);
	ConcreteObserver observer2(2);

	subject.RegisterObserver(&observer1);
	subject.RegisterObserver(&observer2);

	subject.SetValue(123);

	subject.RemoveObserver(&observer1);

	subject.SetValue(456);

	return 0;
}

        在方法RegisterObserver和RemoveObserver中使用了unique_lock来获取互斥锁,保护观察者列表线程安全。 

四、线程池实现

学以致用C++设计模式 之 “观察者模式” - 腾讯云开发者社区-腾讯云

五、简单的电商订阅系统

#include <iostream>
#include <string>
#include <vector>
#include <functional>

class Observable;

class Observer {
public:
	virtual ~Observer() {}
	virtual void Update(const Observable& obs) = 0;
};

class Observable {
public:
	void RegisterObserver(Observer* observer) {
		observers_.push_back(observer);
	}

	void NotifyObservers() const {
		for (const auto& observer : observers_) {
			observer->Update(*this);
		}
	}

private:
	std::vector<Observer*> observers_;
};

class Product : public Observable {
public:
	Product(const std::string& name, double price)
		: name_(name), price_(price) {}

	void SetPrice(double price) {
		price_ = price;
		NotifyObservers();
	}

	std::string GetName() const { return name_; }
	double GetPrice() const { return price_; }

private:
	std::string name_;
	double price_;
};

class Subscriber : public Observer {
public:
	Subscriber(const std::string& name, Product& product)
		: name_(name), product_(product) {
		product_.RegisterObserver(this);
	}

	void Update(const Observable& obs) override {
		std::cout << name_ << ": Product " << product_.GetName()
			<< " price changed to " << product_.GetPrice() << std::endl;
	}

private:
	std::string name_;
	Product& product_;
};

int main() {
	Product laptop("Laptop", 1000.0);
	Subscriber subscriber1("Subscriber 1", laptop);
	Subscriber subscriber2("Subscriber 2", laptop);

	laptop.SetPrice(900.0);
	laptop.SetPrice(950.0);
	return 0;
}

        

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

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

相关文章

MySQL数据同步到ES集群(MySQL数据库与ElasticSearch全文检索的同步)

简介&#xff1a;MySQL数据库与ElasticSearch全文检索的同步&#xff0c;通过binlog的设置对MySQL数据库操作的日志进行记录&#xff0c;利用Python模块对日志进行操作&#xff0c;再利用kafka的生产者消费者模式进行订阅&#xff0c;最终实现MySQL与ElasticSearch间数据的同步…

C++类和对象:面向对象编程的核心。| 面向对象还编什么程啊,活该你是单身狗。

&#x1f451;专栏内容&#xff1a;C学习笔记⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;日拱一卒&#xff0c;功不唐捐 文章目录一、前言二、面向对象编程三、类和对象1、类的引入2、类的定义Ⅰ、声明和定义在一起Ⅱ、声明和定义分开Ⅲ、成员变量命…

ChatGPT 怎么注册使用最新详细教程-新手小白

2022年11月30日chatGPT发布&#xff0c;一年时间风靡全美&#xff0c;甚至有调查&#xff0c;美国89%的大学生用chatGPT做作业&#xff0c;微软用100亿美元投资了该公司&#xff0c;这也引起了google的紧张&#xff0c;神经语言、人工智能、颠覆未来&#xff0c;成为描述chatGP…

VR博物馆带你走进云端,感受数字时代的力量

博物之志&#xff0c;以文化人&#xff0c;为了打破传统线上静态的博物馆图片&#xff0c;VR博物馆给民众带来了全新的视听体验&#xff0c;突破天气、交通、客流量等传统旅游限制问题&#xff0c;在VR全景中还能将线下博物馆的多媒体影响也逐一呈现出来&#xff0c;接下来让我…

ChatGPT给程序员人手一个,这很朋克(由ChatGPT编写)

目录ChatGPT、程序员、朋克为什么程序员需要ChatGPT&#xff0c;为什么这很朋克总结ChatGPT、程序员、朋克 本文由ChatGPT编写。 ChatGPT是由OpenAI开发的大型语言模型。它的核心功能是生成人类语言文本&#xff0c;因此有多种应用场景&#xff0c;如文本生成、对话生成、文本…

FlexGanttFX 11.12.6 Crack

FlexGanttFX 是 JavaFX 的调度和资源规划组件。它允许开发人员通过 CSS 以及可插入渲染器和编辑策略的使用来自定义其外观和行为的每个方面。FlexGanttFX 利用场景图/场景节点和画布 API 的完美组合&#xff0c;确保即使是最大的数据集也可以快速呈现。FlexGanttFX 不仅外表漂亮…

【java】遍历set集合,iterator遍历TreeSet,增强for循环遍历,set排序

目录 1. 增强for循环遍历&#xff08;底层还是用iterator实现的&#xff09;2.iterator遍历TreeSet3.说明4.补充测试用的集合来自上篇&#xff1a;https://blog.csdn.net/qq_43622777/article/details/128924730 1. 增强for循环遍历&#xff08;底层还是用iterator实现的&#…

服务异步通信 RabbitMQ

服务异步通信 RabbitMQRabbitMQ快速入门RabbitMQ概述和安装常见消息模型HelloWorld案例SpringAMQPBasic Queue 简单队列模型消息发送消息接收测试WorkQueue消息发送消息接收测试能者多劳总结发布/订阅Fanout声明队列和交换机消息发送消息接收总结Direct基于注解声明队列和交换机…

Ubuntu 22.04 LTS 入门安装配置优化、开发软件安装一条龙

Ubuntu 22.04 LTS 入门安装配置&优化、开发软件安装 例行前言   最近在抉择手上空余的笔记本&#xff08;X220 i7-2620M&#xff0c;Sk Hynix ddr3 8G*2 &#xff0c;Samsung MINISATA 256G&#xff09;拿来运行什么系统比较好&#xff0c;早年间我或许还会去继续使用Win…

urllib基础+xpath基础(爬虫基础_1)

文章目录1 urllib库的使用1.1 urllib.request发送请求获得响应数据一个类型六个方法内容下载定制请求对象1.2 urllib.parseget请求编码post请求编码1.3 ajax的get请求示例1.4 ajax的post请求示例1.5 Handler处理器1.6 代理服务器2 解析2.1 xpath2.2 JsonPath2.3 BeautifulSoup1…

自动驾驶感知——多传感器融合技术

文章目录1. 运动感知类与环境感知类传感器2. 为什么需要这么多传感器&#xff1f;2.1 从需求侧分析2.2 从供给侧分析3. 多传感器硬件系统的设计思路4. 多传感器系统的时序闭环4.1 传感器时钟闭环构建4.2 成像同步机制5. 多传感器融合算法5.1 多传感器融合问题建模5.2 后融合5.2…

OpenAI ChatGPT 人工智能机器人注册使用,能以中文对答如流的机器人

文章目录一、什么是 ChatGPT二、宇宙最强技术狂魔 马斯克 与 ChatGPT三、在中国大陆如何注册 ChatGPT1. 注册前准备&#xff08;只适用于中国大陆&#xff09;2. 注册方法与步骤四、GhatGPT 的使用方法1. 网页直接使用2. 使用 Google Chrome 浏览器插件3. CSDN 已经接入 ChatGP…

创业平台推荐 ⌈ 适和全部开发者 ⌋ | 成为一名开发者原来那么简单 | 获取收益不再困难 | 快来加入这个大家庭吧

&#x1f49b; 前情提要&#x1f49b; 本文是番外篇&#xff1a;在当今生活中&#xff0c;我们都想在业余时间通过不断学习去充实自己、提高自己 而本文就是为大家拓宽一种思路&#x1f929;&#xff0c;从身为开发者的角度出发&#xff0c;为大家提供一个全面的平台去开启“…

const在C和C++中的区别

昨天有个学生去做C/C软件工程师的笔试题&#xff0c;遇到了这么一个题目&#xff0c;来问我结果是多少&#xff1f; 看似非常普通的一道C语言题目&#xff0c;如果不指定编译器&#xff0c;还真不知道结果是多少。 不信我来演示给你看下。 首先是用gcc来编译&#xff0c;就是…

Linux系统安全:安全技术和防火墙

目录 一、安全技术 1、安全技术 2、防火墙分类 二、防火墙 1、iptables五表五链 2、黑白名单 3、iptables基本语法 4、iptables选项 5、控制类型 6、隐藏扩展模块 7、显示扩展模块 8、iptables规则保存 9、自定义链使用 一、安全技术 1、安全技术 ①入侵检测系统…

Node.js http 模块详解(1)

http 模块 使用 Node.js 中创建 Web 服务&#xff0c;主要依赖内置的 http 模块。经典的 express.js、koa.js 框架都是以 http 模块为核心&#xff0c;进行的不同程度的封装。 创建一个最简单的 Web 服务只需要几行代码。新建一个 index.js 文件&#xff0c;输入以下内容&…

【GCC】3: webrtc带宽(预估调整)和GCC模块

webrtc源码分析(8)-拥塞控制(上)-码率预估 bandwidth bitrate estimator 整体码控流程 webrtc源码分析(8)-拥塞控制(上)-码率预估 大神绘制的: TWCC TCC算法的流程 TccEstimator 大神用go写的:Transport-CC Algorithm Description This is a Goog

python小游戏——打砖块代码开源

♥️作者&#xff1a;小刘在这里 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽放&#xff0c;愿所有的美好&#…

Redis单线程还快的原因

Redis单线程还快的原因 Redis Server是多线程的&#xff0c;Redis单线程指的是请求处理整个流程是单线程的&#xff01; 单线程还快的原因 纯内存操作&#xff1a; Redis数据存储在内存中&#xff0c;速度很快。 非阻塞IO多路复用机制&#xff1a; Redis 采用了多路复用机制&a…

从事互联网行业,考一个PMP会有帮助吗?

PMP是项目管理类证书&#xff0c;不要求工作岗位&#xff0c;只要涉及项目、项目管理岗位&#xff0c;就可以考PMP证书&#xff0c;就会有帮助。但这里先说一下PMP的报名条件&#xff0c;先看能不能考&#xff0c;再说考了是否有帮助。 条件如下图&#xff0c;有两个&#xff…