观察者模式(发布-订阅)是行为型模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象,当主题对象的状态发生变化的时候,所有依赖于它的观察者都得到通知并被自动更新。
我记得软件工程课里面讲了一个软件设计架构——事件总线模式,又叫出版商-订阅制,英文是Publish-Subscribe。本质是引入中间媒介,避免直接调用,增强独立性(公共数据总线)
举个🌰,引入一个邮局作为中间商,新的订阅者只需要在邮局注册,由邮局统一转发就可以,不需要出版商直接发货
在Windows中,注册表就是出版商-订阅制,根据不同文件名后缀,OS查阅公共注册表来决定用什么软件打开,否则就是要改OS的源代码。
软件开发和软件架构的模式好像是互通的,不得不说计算机是一门统一和谐的科学。
回到我们的设计模式,观察者模式依赖两个模块:
- 主题(Subject):也就是被观察的对象,他可以维护一组观察着,当主题本身发生改变时,就会通知观察者。
- 观察者(Observer):观察主题的对象,当“被观察”的主题发生变化时,观察者就会得到通知并执行相应的处理。
基本结构:
- 主题
Subject
:定义成一个接口,提供方用于注册、删除和通知观察者,通常也包含一个状态,当这个状态发生变化时,通知所有的观察者。 - 观察者
Observer
:观察者也需要实现一个接口,包含一个更新方法,在接受主题通知时执行对应的操作。 - 具体主题:实现上述主题的接口
- 具体观察者:实现上述观察者接口
代码实现
/*** 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.
这段代码首先定义了抽象的IObserver
和ISubject
类,然后分别public
继承生成各自的派生类。在派生类中重写了所有的虚函数,如果有遗漏就无法创建实例。
Subject
类就是实现attach
,detach
,还有createMessage
和notify
这几个接口,对应于注册、移除、创建消息通知观察者的功能,使用list
存储观察者。
Observer
类实例初始化是需要指定主题Subject
,然后构造函数添加到该Subject
的列表里,observer
也可以使用RemoveMeFromTheList
取消订阅。析构的时候输出其对应的编号num
。
业务逻辑就是,当Subject
类创建新的message
会调用notify
,然后所有的observer
都会update
自己收到的信息。
最后说一下该设计模式的优缺点
优点:
- 开闭原则。 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。
- 你可以在运行时建立对象之间的联系。
缺点:
- 订阅者的通知顺序是随机的。