C++11改进观察者模式

news/2024/11/16 14:37:03/

        观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

        先看一个简单的观察者模式是如何实现的:

#include <iostream>
#include <vector>
#include <map>
using namespace std;class Base
{
public:Base(){}virtual ~Base(){}virtual void update(int a, int b) = 0;
};class Devices1 : public Base
{
public:virtual void update(int a, int b){cout << "Devices1 " << a << "\t" << b << endl;}
};class Devices2 : public Base
{
public:virtual void update(int a, int b){cout << "Devices2 " << a << "\t" << b << endl;}
};class Subject
{
public:void attach(int iKey, Base* pbase){m_map[iKey] = pbase;}void detach(int iKey){auto it = m_map.find(iKey);if (it != m_map.end()){m_map.erase(it);}}void notify(int a, int b){auto it = m_map.begin();while(it != m_map.end()){it->second->update(a, b);it++;}}private:map<int, Base*> m_map;
};int main()
{Subject sub;Devices1 d1;Devices2 d2;sub.attach(1, &d1);sub.attach(2, &d2);sub.notify(1, 2);return 0;
}

        这个例子很简单,是一种经典的观察者模式的实现,但是这种实现不够通用,只能对特定的观察者才有效,即必须是Base抽象类的派生类才行,并且这个观察者类还只能带2个int类型的参数。这种实现有两个限定:1、需要继承,继承是强对象关系,不够灵活;2、观察者被通知的接口参数不支持变化,导致观察者不能应付接口的变化。

        我们可以通过C++11做一些改进,主要改进的地方有两个:通过被通知接口参数化和std::function来代替继承;通过可变参数模板和完美转发来消除接口变化产生新的影响。

        使用C++11改良的观察者模式,代码如下所示:

#include <iostream>
#include <vector>
#include <map>
#include <functional>
#include <string>
using namespace std;template<typename Func>
class Events
{
public:Events(){};~Events(){};int Connect(Func&& f){return Assign(f);}int Connect(const Func& f){return Assign(f);}void Disconnect(int key){m_connections.erase(key);}///通知所有观察者好template<typename ...Args>void Notify(Args&& ...args){for(auto& it : m_connections){it.second(std::forward<Args>(args)...);}}private:///给观察者分配编号,在多线程环境需要加锁template<typename F>int Assign(F&& f){int k = m_observerId++;m_connections.emplace(k, std::forward<F>(f));return k;}int m_observerId = 0;std::map<int, Func> m_connections;};void print(int a, int b)
{cout << "func " <<  a << ", " << b << endl;
}class A
{
public:void print(int a, int b){cout << "class " << a << ", " << b << endl;}
};int main()
{Events<std::function<void(int, int)>> myevent;auto key = myevent.Connect(print);A a;std::function<void(int, int)> f = std::bind(&A::print, &a, std::placeholders::_1, std::placeholders::_1);auto key1 = myevent.Connect(f);///lambda注册auto lamkey = myevent.Connect([](int a, int b){cout << "lambda  " << a << ", " << b << endl;});myevent.Notify(1, 2);return 0;
}

        C++11实现的观察者模式,内部维护了一个泛型函数列表,观察者只需要将观察者函数注册进来即可,消除了继承导致的强耦合。        


http://www.ppmy.cn/news/1263359.html

相关文章

用Java的Process执行命令行,ffmpeg抽帧到一千多帧图片卡住,不报错。

项目场景&#xff1a; 最近需要写一个抽帧、推流的工具类&#xff0c;抽帧写好测试的时候也没问题&#xff0c;等到真正用的时候就发现各种问题。 问题描述 用Java执行ffmpeg抽帧命令&#xff0c;测试的时候没有问题&#xff0c;后来发现抽帧图片多了就会卡住。 刚开始觉得可…

使用wire重构商品微服务

一.wire简介 Wire 是一个轻巧的Golang依赖注入工具。它由Go Cloud团队开发&#xff0c;通过自动生成代码的方式在编译期完成依赖注入。 依赖注入是保持软件 “低耦合、易维护” 的重要设计准则之一。 此准则被广泛应用在各种开发平台之中&#xff0c;有很多与之相关的优秀工…

第五章---创建个人中心页面(上)

1. 整体框架 2. 新建表 bot 在数据库中新建表 bot 表中包含的列&#xff1a; id: int&#xff1a;非空、自动增加、唯一、主键 pojo 中定义主键的注解&#xff1a;TableId(type IdType.AUTO) user_id: int&#xff1a;非空 注意&#xff1a;在 pojo 中需要定义成 userId…

RFID在新能源工厂大放异彩

RFID在新能源工厂大放异彩 我国在十四五规划中提出了建设绿色低碳发展的目标&#xff0c;新能源产业成为了国家发展的重点领域之一&#xff0c;开始大力支持各种新能源厂商发展。各个厂商之间不仅比产品、比技术。也比生产想要降本增效&#xff0c;为了实现这一目标&#xff0…

InnoDB Architecture MySQL 5.7 vs 8.0

innodb-architecture-5-7 innodb-architecture-8-0 图片均来源于MySQL官网

2.1 Linux C 编程

一、Hello World 1、在用户根目录下创建一个C_Program&#xff0c;并在这里面创建3.1文件夹来保存Hellow World程序&#xff1b; 2、安装最新版nvim ①sudo apt-get install ninja-build gettext cmake unzip curl ②sudo apt install lua5.1 ③git clone https://github.…

Redis基础系列-持久化

Redis基础系列-持久化 文章目录 Redis基础系列-持久化1. 什么是持久化2. 为什么要持久化3. 持久化的两种方式3.1 持久化方式1&#xff1a;RDB(redis默认持久化方式)3.11 配置步骤-自动触发3.12 配置步骤-手动触发3.12 优点3.13 缺点3.14 检查和修复RDB快照文件3.15 哪些情况会触…

2024年AI视频识别技术的6大发展趋势预测

随着人工智能技术的快速发展&#xff0c;AI视频识别技术也将会得到进一步的发展和应用。2023年已经进入尾声&#xff0c;2024年即将来临&#xff0c;那么AI视频识别技术又将迎来怎样的发展趋势&#xff1f;本文将对2023年的AI视频技术做一个简单的盘点并对2024年的发展趋势进行…