设计模式——观察者模式

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

我记得软件工程课里面讲了一个软件设计架构------事件总线模式,又叫出版商-订阅制,英文是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自己收到的信息。

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

优点:

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

缺点:

  • 订阅者的通知顺序是随机的。
相关推荐
45W冲冲冲40 分钟前
WIN10+CMAKE+MinGW+Opencv/C++ 和VScode开发环境搭建
c++·vscode·opencv
春蕾夏荷_7282977251 小时前
MFC 对话框中显示CScrollView实例
c++·mfc·cscrollview
小白不太白9502 小时前
设计模式之 命令模式
设计模式·命令模式
吃汉堡吃到饱2 小时前
【创建型设计模式】单例模式
单例模式·设计模式
小白不太白9502 小时前
设计模式之 备忘录模式
服务器·设计模式·备忘录模式
zzzhpzhpzzz2 小时前
设计模式——策略模式
设计模式·策略模式
入门到跑路2 小时前
【君正T31开发记录】8.了解rtsp协议及设计模式
网络协议·设计模式
小白不太白9502 小时前
设计模式之 解释器模式
java·设计模式·解释器模式
originalHSL2 小时前
Load-Balanced-Online-OJ(负载均衡式在线OJ)
linux·c++·git·vscode·http·https·json
zhangzhangkeji3 小时前
C++ function 源码分析(5):is_const_v<const 函数> = False ,源码注释及资源
开发语言·c++·stl 库源码