当前位置: 首页 > article >正文

C++11改进观察者模式

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

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

#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.kler.cn/news/160373.html

相关文章:

  • js 将后端返回的对象转换为数组
  • VUEX使用总结
  • spark log4j日志配置
  • Amazon CodeWhisperer 正式可用, 并面向个人开发者免费开放
  • redis应用-分布式锁
  • Java - InetAddress#isReachable 方法解析
  • EPICS modbus 模块数字量读写练习
  • 分类与群组:解析分类和聚类分析技术
  • Kubernetes入门笔记——(2)k8s设计文档
  • java之stringbuf
  • 【9】PyQt对话框
  • Ubuntu 20.04 安装 mysql8 LTS
  • 【AI-ChatGPT-Prompt】什么是Prompt
  • Redis生产实战-热key、大key解决方案、数据库与缓存最终一致性解决方案
  • Centos7如何安装MySQL
  • HBase-架构与设计
  • 面试冲刺 - 算法题 1
  • 大数据生态架构:探索未来科技的无限可能。
  • Word文件设置了只读模式,为什么还能编辑?
  • 开发重要网站
  • 同旺科技 USB TO RS-485 定制款适配器--- 拆解(四)
  • 要求CHATGPT高质量回答的艺术:提示工程技术的完整指南—第 4 章:控制温度和 Top-p 采样
  • k8s 安装 Longhorn
  • 【数据结构】动态规划(Dynamic Programming)
  • qt 5.15.2 网络文件下载功能
  • Pair<T, U>
  • Ubuntu22.04 安装nvida-docker2和改路径
  • 分布式数据库HBase
  • 使用Go快速开发TCP公共服务
  • 深信服技术认证“SCSA-S”划重点:XSS漏洞