设计模式——观察者模式

观察者模式(发布-订阅)是行为型模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象,当主题对象的状态发生变化的时候,所有依赖于它的观察者都得到通知并被自动更新。

我记得软件工程课里面讲了一个软件设计架构------事件总线模式,又叫出版商-订阅制,英文是Publish-Subscribe。本质是引入中间媒介,避免直接调用,增强独立性(公共数据总线)

举个🌰,引入一个邮局作为中间商,新的订阅者只需要在邮局注册,由邮局统一转发就可以,不需要出版商直接发货

在Windows中,注册表就是出版商-订阅制,根据不同文件名后缀,OS查阅公共注册表来决定用什么软件打开,否则就是要改OS的源代码。

软件开发和软件架构的模式好像是互通的,不得不说计算机是一门统一和谐的科学。

回到我们的设计模式,观察者模式依赖两个模块:

  • 主题(Subject):也就是被观察的对象,他可以维护一组观察着,当主题本身发生改变时,就会通知观察者。
  • 观察者(Observer):观察主题的对象,当"被观察"的主题发生变化时,观察者就会得到通知并执行相应的处理。

基本结构:

  • 主题Subject:定义成一个接口,提供方用于注册、删除和通知观察者,通常也包含一个状态,当这个状态发生变化时,通知所有的观察者。
  • 观察者Observer:观察者也需要实现一个接口,包含一个更新方法,在接受主题通知时执行对应的操作。
  • 具体主题:实现上述主题的接口
  • 具体观察者:实现上述观察者接口

代码实现

cpp 复制代码
/**
 * Observer Design Pattern
 *
 * Intent: Lets you define a subscription mechanism to notify multiple objects
 * about any events that happen to the object they're observing.
 *
 * Note that there's a lot of different terms with similar meaning associated
 * with this pattern. Just remember that the Subject is also called the
 * Publisher and the Observer is often called the Subscriber and vice versa.
 * Also the verbs "observe", "listen" or "track" usually mean the same thing.
 */

#include <iostream>
#include <list>
#include <string>

class IObserver {
 public:
  virtual ~IObserver(){};
  virtual void Update(const std::string &message_from_subject) = 0;
};

class ISubject {
 public:
  virtual ~ISubject(){};
  virtual void Attach(IObserver *observer) = 0;
  virtual void Detach(IObserver *observer) = 0;
  virtual void Notify() = 0;
};

/**
 * The Subject owns some important state and notifies observers when the state
 * changes.
 */

class Subject : public ISubject {
 public:
  virtual ~Subject() {
    std::cout << "Goodbye, I was the Subject.\n";
  }

  /**
   * The subscription management methods.
   */
  void Attach(IObserver *observer) override {
    list_observer_.push_back(observer);
  }
  void Detach(IObserver *observer) override {
    list_observer_.remove(observer);
  }
  void Notify() override {
    std::list<IObserver *>::iterator iterator = list_observer_.begin();
    HowManyObserver();
    while (iterator != list_observer_.end()) {
      (*iterator)->Update(message_);
      ++iterator;
    }
  }

  void CreateMessage(std::string message = "Empty") {
    this->message_ = message;
    Notify();
  }
  void HowManyObserver() {
    std::cout << "There are " << list_observer_.size() << " observers in the list.\n";
  }

  /**
   * Usually, the subscription logic is only a fraction of what a Subject can
   * really do. Subjects commonly hold some important business logic, that
   * triggers a notification method whenever something important is about to
   * happen (or after it).
   */
  void SomeBusinessLogic() {
    this->message_ = "change message message";
    Notify();
    std::cout << "I'm about to do some thing important\n";
  }

 private:
  std::list<IObserver *> list_observer_;
  std::string message_;
};

class Observer : public IObserver {
 public:
  Observer(Subject &subject) : subject_(subject) {
    this->subject_.Attach(this);
    std::cout << "Hi, I'm the Observer \"" << ++Observer::static_number_ << "\".\n";
    this->number_ = Observer::static_number_;
  }
  virtual ~Observer() {
    std::cout << "Goodbye, I was the Observer \"" << this->number_ << "\".\n";
  }

  void Update(const std::string &message_from_subject) override {
    message_from_subject_ = message_from_subject;
    PrintInfo();
  }
  void RemoveMeFromTheList() {
    subject_.Detach(this);
    std::cout << "Observer \"" << number_ << "\" removed from the list.\n";
  }
  void PrintInfo() {
    std::cout << "Observer \"" << this->number_ << "\": a new message is available --> " << this->message_from_subject_ << "\n";
  }

 private:
  std::string message_from_subject_;
  Subject &subject_;
  static int static_number_;
  int number_;
};

int Observer::static_number_ = 0;

void ClientCode() {
  Subject *subject = new Subject;
  Observer *observer1 = new Observer(*subject);
  Observer *observer2 = new Observer(*subject);
  Observer *observer3 = new Observer(*subject);
  Observer *observer4;
  Observer *observer5;

  subject->CreateMessage("Hello World! :D");
  observer3->RemoveMeFromTheList();

  subject->CreateMessage("The weather is hot today! :p");
  observer4 = new Observer(*subject);

  observer2->RemoveMeFromTheList();
  observer5 = new Observer(*subject);

  subject->CreateMessage("My new car is great! ;)");
  observer5->RemoveMeFromTheList();

  observer4->RemoveMeFromTheList();
  observer1->RemoveMeFromTheList();

  delete observer5;
  delete observer4;
  delete observer3;
  delete observer2;
  delete observer1;
  delete subject;
}

int main() {
  ClientCode();
  return 0;
}
Hi, I'm the Observer "1".
Hi, I'm the Observer "2".
Hi, I'm the Observer "3".
There are 3 observers in the list.
Observer "1": a new message is available --> Hello World! :D
Observer "2": a new message is available --> Hello World! :D
Observer "3": a new message is available --> Hello World! :D
Observer "3" removed from the list.
There are 2 observers in the list.
Observer "1": a new message is available --> The weather is hot today! :p
Observer "2": a new message is available --> The weather is hot today! :p
Hi, I'm the Observer "4".
Observer "2" removed from the list.
Hi, I'm the Observer "5".
There are 3 observers in the list.
Observer "1": a new message is available --> My new car is great! ;)
Observer "4": a new message is available --> My new car is great! ;)
Observer "5": a new message is available --> My new car is great! ;)
Observer "5" removed from the list.
Observer "4" removed from the list.
Observer "1" removed from the list.
Goodbye, I was the Observer "5".
Goodbye, I was the Observer "4".
Goodbye, I was the Observer "3".
Goodbye, I was the Observer "2".
Goodbye, I was the Observer "1".
Goodbye, I was the Subject.

这段代码首先定义了抽象的IObserverISubject类,然后分别public继承生成各自的派生类。在派生类中重写了所有的虚函数,如果有遗漏就无法创建实例。
Subject类就是实现attachdetach,还有createMessagenotify这几个接口,对应于注册、移除、创建消息通知观察者的功能,使用list存储观察者。
Observer类实例初始化是需要指定主题Subject,然后构造函数添加到该Subject的列表里,observer也可以使用RemoveMeFromTheList取消订阅。析构的时候输出其对应的编号num

业务逻辑就是,当Subject类创建新的message会调用notify,然后所有的observer都会update自己收到的信息。

最后说一下该设计模式的优缺点

优点:

  • 开闭原则。 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。
  • 你可以在运行时建立对象之间的联系。

缺点:

  • 订阅者的通知顺序是随机的。
相关推荐
付聪12101 小时前
策略模式介绍和代码示例
设计模式
Dream it possible!1 小时前
LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_34_中等_C++)(二分查找)(一次二分查找+挨个搜索;两次二分查找)
c++·算法·leetcode
柠石榴1 小时前
【练习】【回溯No.1】力扣 77. 组合
c++·算法·leetcode·回溯
王老师青少年编程1 小时前
【GESP C++八级考试考点详细解读】
数据结构·c++·算法·gesp·csp·信奥赛
ThereIsNoCode3 小时前
「软件设计模式」状态模式(State)
设计模式·状态模式
澄澈天空3 小时前
C++ MFC添加RichEditControl控件后,程序启动失败
c++·mfc
Lzc7744 小时前
C++初阶——简单实现vector
c++·简单实现vector
一个小白14 小时前
C++——list模拟实现
开发语言·c++
程序员老舅5 小时前
C++ Qt项目教程:WebServer网络测试工具
c++·qt·测试工具·webserver·qt项目·qt项目实战
靡不有初1115 小时前
CCF-CSP第18次认证第一题——报数【两个与string相关的函数的使用】
c++·学习·ccfcsp