Observer(观察者模式)

1. 意图

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

在观察者模式中,有两类对象:被观察者(Subject)和观察者(Observer)。被观察者是一个具有状态的对象,当其状态发生变化时,会通知所有的观察者。观察者是一个依赖于被观察者的对象,它会接收到被观察者的通知,并进行相应的处理。

2. 适用性

《GOF设计模式:可复用面向对象软件的基础》中描述如下:

  • 当一个抽象模型有两方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们各自独立地自主改变和复用。
  • 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
  • 当一个对象必须通知其它对象,而它又不能假定对象是谁。即对象之间关系是解耦合的。

3. 实现

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

class Observer {
public:
  virtual ~Observer() {}

  virtual void Update(const std::string &msg) = 0;
};

class NetObserver : public Observer {
public:
  virtual void Update(const std::string &msg) override {
    std::cout << "NetObserver recv msg: " << msg << std::endl;
  }
};

class SerialObserver : public Observer {
public:
  virtual void Update(const std::string &msg) override {
    std::cout << "SerialObserver recv msg: " << msg << std::endl;
  }
};

class Subject {
public:
  virtual ~Subject() {}

  bool Attach(Observer *ptr) {
    if (ptr == nullptr)
      return false;
    m_vecObserver.push_back(ptr);
    return true;
  }
  void Detach(const Observer *ptr) {
    for (auto iter = m_vecObserver.begin(); iter != m_vecObserver.end();
         ++iter) {
      if (*iter != ptr)
        continue;
      m_vecObserver.erase(iter);
    }
  }
  void Notify(const std::string &msg) {
    for (Observer *ptr : m_vecObserver)
      ptr->Update(msg);
  }

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

class NetSubject : public Subject {
public:
  void DoSomething() { Notify("NetSubject msg"); }
};

class SerialSubject : public Subject {
public:
  void DoSomething() { Notify("SerialSubject msg"); }
};

void Test() {
  NetObserver *net1 = new NetObserver;
  NetObserver *net2 = new NetObserver;
  SerialObserver *serial1 = new SerialObserver;
  SerialObserver *serial2 = new SerialObserver;

  NetSubject *netSub = new NetSubject;
  SerialSubject *serialSub = new SerialSubject;

  netSub->Attach(net1);
  netSub->DoSomething();
  std::cout << "------------------------------" << std::endl;
  netSub->Attach(net2);
  netSub->DoSomething();
  std::cout << "------------------------------" << std::endl;

  serialSub->Attach(serial1);
  serialSub->DoSomething();
  std::cout << "------------------------------" << std::endl;
  serialSub->Attach(serial2);
  serialSub->DoSomething();

  delete net1;
  delete net2;
  delete serial1;
  delete serial2;
  delete netSub;
  delete serialSub;
}

int main() {
  Test();
  return 0;
}

执行结果

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| NetObserver recv msg: NetSubject msg ------------------------------ NetObserver recv msg: NetSubject msg NetObserver recv msg: NetSubject msg ------------------------------ SerialObserver recv msg: SerialSubject msg ------------------------------ SerialObserver recv msg: SerialSubject msg SerialObserver recv msg: SerialSubject msg |

4. 优缺点

  • 松耦合性:被观察者和观察者之间是松耦合的,它们之间仅通过接口或抽象类进行通信,不需要直接相互引用,可以独立变化。

  • 可扩展性:可以随时增加新的观察者,而不需要修改被观察者的代码,也可以很容易地添加新的被观察者。

  • 可重用性:观察者模式可以在不同的场景中重复使用,不需要重写逻辑。

  • 实时更新:被观察者一旦发生变化,所有的观察者都会实时收到通知并进行相应的更新,保证了数据的实时性。

  • 观察者过多:如果观察者的数量过多,通知所有观察者可能会导致性能问题,影响系统的运行效率。

  • 顺序控制困难:观察者之间是一种松散的耦合关系,无法控制观察者接收通知的顺序,可能会导致一些不可预测的问题。

  • 循环依赖问题:如果观察者与被观察者之间存在循环依赖,可能会导致系统出现问题。

5. 模板实现

cpp 复制代码
#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>

class ObserverA {
public:
  void UpdateA(const std::string &msg) {
    std::cout << "ObserverA msg: " << msg << std::endl;
  }
};

class ObserverB {
public:
  void UpdateB(const std::string &msg) {
    std::cout << "ObserverB msg: " << msg << std::endl;
  }
};

template <typename Func> class Event {
public:
  Event() : m_observeId(0) {}
  virtual ~Event() {}

  int Attach(Func &&f) { return Assign(f); }
  int Attach(const Func &f) { return Assign(f); }
  void Detach(int key) { m_mapOberserve.erase(key); }
  template <typename... Args> void Notify(Args &&...args) {
    for (auto &it : m_mapOberserve) {
      it.second(std::forward<Args>(args)...);
    }
  }

private:
  Event(const Event &) = delete;
  Event &operator=(const Event &) = delete;

  template <typename F> int Assign(F &&f) {
    m_mapOberserve.emplace(m_observeId++, std::forward<F>(f));
    return m_observeId - 1;
  }

private:
  int m_observeId;
  std::unordered_map<int, Func> m_mapOberserve;
};
void Test() {
  Event<std::function<void(const std::string &)>> event;
  ObserverA oba;
  int key =
      event.Attach(std::bind(&ObserverA::UpdateA, oba, std::placeholders::_1));
  event.Notify("AAAAA");
  std::cout << "-----------------------" << std::endl;
  ObserverB obb;

  event.Attach(std::bind(&ObserverB::UpdateB, obb, std::placeholders::_1));
  event.Notify("AAAAABBBBB");
  std::cout << "-----------------------" << std::endl;
  event.Detach(key);
  event.Notify("BBBBB");
}

int main() {
  Test();
  return 0;
}

执行输出

|-----------------------------------------------------------------------------------------------------------------------------------------------|
| ObserverA msg: AAAAA ----------------------- ObserverB msg: AAAAABBBBB ObserverA msg: AAAAABBBBB ----------------------- ObserverB msg: BBBBB |

相关推荐
2501_941875283 小时前
从配置中心到动态管理的互联网工程语法演进与多语言实践分享
开发语言·python
hfywmsj4 小时前
广州餐饮铺位招租决策模型:多因子选址系统设计
开发语言·人工智能·python·广州餐饮铺位招租
努力的lpp6 小时前
php反序列化一(大白话版)
开发语言·web安全·php·ctf·反序列化
菜鸟~noob2336 小时前
【电子战】第03篇:CFAR(恒虚警)处理【含matlab代码】
开发语言·matlab
泡海椒7 小时前
内置SPI函数库详解:JQuick-Java Builtin工具类实战用法
java·开发语言·python
hqyjzsb7 小时前
零 AI 项目经验,学 Python 转型 AI 的正确顺序是什么?
开发语言·人工智能·python·算法·职场和发展·数据挖掘·数据分析
Keven_118 小时前
算法札记:ACM适用的很骚的C++语法(持续更新)
开发语言·c++
mengzhi啊8 小时前
QPluginLoader 动态 DLL 插件版,支持插件之间通信,广播。请求和应答
c++·qt
脉动数据行情18 小时前
C# 实现德指 DAX 期货 WebSocket 实时行情监听
开发语言·websocket·c#
大圣编蚕9 小时前
Java ByteArrayInputStream 详解:从入门到实战
java·开发语言·算法