從C++編程入手設計模式——觀察者模式
? 觀察者模式簡直就是字如其名,觀察觀察,觀察到了告訴別人。觀察手的作用如此,觀察者模式的工作機制也是如此。這個模式的核心思路是:一個對象的狀態發生變化時,自動通知依賴它的其他對象,讓它們自行更新。
主要的組成部分
一是被觀察者,也叫主題(Subject),它負責記錄觀察者并在自身狀態發生變化時通知它們;換而言之,Subject作為信息的生成端,生成信息。
二是觀察者(Observer),它主動注冊到被觀察者中,等待被通知,一旦收到消息就立即采取行動。這里生成端會使用遠程的方式通知我們的被觀察者投送信息。
? 你可以把觀察者模式想象成“訂閱報紙”。報社就是主題,它維護一個訂閱者名單(觀察者列表)。每當有新一期的報紙(數據更新)出版時,它就向所有訂閱者投遞(通知)。每位訂閱者收到報紙后根據自己的需要決定怎么處理,是直接閱讀、剪報存檔還是轉發朋友。這種發布-訂閱機制就是觀察者模式的最基本形式。
在 C++ 中怎么寫?
觀察者模式的代碼實現非常自然。我們會先定義一個抽象的觀察者接口(通常叫 Observer
或者 Sender
),讓不同的具體觀察者實現它;再定義一個主題類(通常叫 Subject
或 Notifier
),它包含一個觀察者列表,并負責注冊、移除和通知觀察者。每當主題內部數據變化(比如溫度、濕度更新),它就會遍歷觀察者列表,挨個調用它們的“更新方法”。觀察者模式的重點不在于通知的“數據內容”本身,而在于自動通知的機制。
選擇觀察者模式?
在簡單的程序中,我們可能會直接調用某些對象來執行邏輯。但如果程序復雜起來,比如某個數據更新后要引發十幾種操作,硬編碼這些調用就會讓系統結構僵硬,維護困難。
觀察者模式將“變化的主體”和“依賴的反應者”解耦。被觀察者不用關心觀察者是誰,它只管發出通知。觀察者自己決定是否接收通知、如何響應。這讓系統的擴展性和靈活性大大提高。
比如天氣系統中新加一個“語音播報設備”,只要實現觀察者接口并注冊進去,其他部分代碼幾乎無需改動。
觀察者模式的幾個關鍵詞
- 低耦合:主題和觀察者之間不互相依賴,只通過接口通信。
- 自動同步:當主題數據變化時,所有觀察者自動得到通知。
- 一對多:一個主題可以對應多個觀察者。
- 注冊/注銷機制:觀察者可以自由加入或離開通知流程。
Example
? 可以直接看一個代碼框架來解決問題
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <algorithm>// 觀察者接口
class Observer {
public:virtual void update(const std::string& message) = 0;virtual ~Observer() = default;
};// 具體觀察者
class ConcreteObserver : public Observer {
private:std::string name;
public:explicit ConcreteObserver(std::string n) : name(std::move(n)) {}void update(const std::string& message) override {std::cout << "Observer [" << name << "] received: " << message << '\n';}
};// 主題接口
class Subject {
public:virtual void attach(std::shared_ptr<Observer> observer) = 0;virtual void detach(std::shared_ptr<Observer> observer) = 0;virtual void notify(const std::string& message) = 0;virtual ~Subject() = default;
};// 具體主題
class ConcreteSubject : public Subject {
private:std::vector<std::shared_ptr<Observer>> observers;
public:void attach(std::shared_ptr<Observer> observer) override {observers.push_back(std::move(observer));}void detach(std::shared_ptr<Observer> observer) override {observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());}void notify(const std::string& message) override {for (const auto& observer : observers) {observer->update(message);}}
};int main() {auto subject = std::make_shared<ConcreteSubject>();auto obs1 = std::make_shared<ConcreteObserver>("Observer1");auto obs2 = std::make_shared<ConcreteObserver>("Observer2");subject->attach(obs1);subject->attach(obs2);subject->notify("Event 1 occurred");subject->detach(obs1);subject->notify("Event 2 occurred");return 0;
}
? 你可以使用這樣的方式來作為事件驅動框架的一個根基。
練習題
天氣站系統
要求:設計一個天氣數據發布系統,天氣站(WeatherStation)是“主題”,可通知多個“觀察者”顯示設備(如手機、網頁等)。
功能要求:
- 觀察者可以注冊/注銷。
- 當溫度或濕度變化時,所有注冊觀察者會收到更新并打印數據。(注:這個部分筆者的參考代碼沒有實現,您自己酌情思考)
提示:
- 使用接口
IObserver
表示觀察者,定義如update(float temperature, float humidity)
; - 主題接口
ISubject
需要支持registerObserver()
、removeObserver()
、notifyObservers()
; - 可以實現兩個觀察者類
PhoneDisplay
和WebDisplay
。
modern-cpp-patterns-playground/Observer/WeatherForecast at main · Charliechen114514/modern-cpp-patterns-playground