观察者模式 (Observer Pattern)与几个C++应用例子

发布于:2025-09-01 ⋅ 阅读:(18) ⋅ 点赞:(0)

1. 模式定义与核心思想
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象的状态发生变化时,它会自动通知所有观察者对象,使它们能够自动更新自己。

核心思想: 解耦主题和观察者。主题不需要知道哪些具体对象在观察它,它只需要维护一个观察者列表,并在状态改变时向它们发送通知。这使得系统更灵活,可以动态地添加和删除观察者,而无需修改主题的代码。

2. 模式结构(角色分析)
观察者模式通常包含以下四个角色:

Subject (主题 / 被观察者):

维护一个观察者(Observer)对象的集合。

提供接口可以添加(Attach)和删除(Detach)观察者。

提供接口用于通知(Notify)所有注册的观察者。

ConcreteSubject (具体主题):

继承自 Subject。

维护其自身的状态(例如,一个温度值、一个鼠标点击位置)。

当状态发生改变时,调用父类的 Notify 方法通知所有观察者。

Observer (观察者):

为所有具体观察者定义一个更新接口。通常是一个抽象的 Update() 方法。

当接到主题的通知时,调用此方法做出响应。

ConcreteObserver (具体观察者):

继承自 Observer。

实现 Update() 方法。在此方法中,通常会从 ConcreteSubject 获取所需的状态,并执行具体的业务逻辑(如更新UI、记录日志等)。

通常会维护一个指向 ConcreteSubject 的引用,以便在更新时获取其状态。

它们之间的协作关系如下图所示:
(这是一个UML协作序列的文本描述)

ConcreteObserver 调用 ConcreteSubject 的 Attach 方法将自己注册到主题的观察者列表中。

ConcreteSubject 的内部状态发生变化。

ConcreteSubject 调用 Notify 方法。

Notify 方法遍历所有注册的 Observer,并调用每个观察者的 Update 方法。

ConcreteObserver 的 Update 方法被调用,它可以通过传入的参数或查询 ConcreteSubject 来获取新状态,并据此更新自身。

3. 经典C++实现示例
下面是一个简单的C++实现,模拟一个气象站(主题)和多个显示设备(观察者)。

#include <iostream>
#include <vector>
#include <string>
#include <memory>

// 前向声明
class Subject;

// 1. Observer (观察者接口)
class Observer {
public:
    virtual ~Observer() = default;
    // 更新接口,参数通常是Subject或状态数据
    virtual void Update(Subject& subject) = 0;
};

// 2. Subject (主题接口)
class Subject {
public:
    virtual ~Subject() = default;
    // 注册观察者
    void Attach(std::shared_ptr<Observer> observer) {
        observers_.push_back(observer);
    }
    // 移除观察者
    void Detach(std::shared_ptr<Observer> observer) {
        // 在实际项目中,这里需要更安全的删除逻辑
        observers_.erase(
            std::remove(observers_.begin(), observers_.end(), observer),
            observers_.end());
    }
    // 通知所有观察者
    void Notify() {
        for (auto& observer : observers_) {
            observer->Update(*this);
        }
    }

private:
    std::vector<std::shared_ptr<Observer>> observers_;
};

// 3. ConcreteSubject (具体主题:气象站)
class WeatherStation : public Subject {
public:
    // 设置状态(温度)并通知观察者
    void SetTemperature(double temp) {
        temperature_ = temp;
        Notify(); // 关键一步:状态改变,立即通知所有观察者
    }

    // 获取状态(供观察者查询)
    double GetTemperature() const {
        return temperature_;
    }

private:
    double temperature_ = 0.0;
};

// 4. ConcreteObserver (具体观察者:手机显示)
class PhoneDisplay : public Observer {
public:
    explicit PhoneDisplay(const std::string& name) : name_(name) {}

    void Update(Subject& subject) override {
        // 安全的向下转型,确认主题类型
        WeatherStation* ws = dynamic_cast<WeatherStation*>(&subject);
        if (ws) {
            double temp = ws->GetTemperature();
            std::cout << "[" << name_ << "] Temperature updated: " << temp << "°C\n";
        }
    }

private:
    std::string name_;
};

// 5. 另一个具体观察者:LED大屏显示
class LedDisplay : public Observer {
public:
    void Update(Subject& subject) override {
        WeatherStation* ws = dynamic_cast<WeatherStation*>(&subject);
        if (ws) {
            double temp = ws->GetTemperature();
            std::cout << "*** LED Display: CURRENT TEMP = " << temp << "°C ***\n";
        }
    }
};

// 客户端代码
int main() {
    // 创建主题(气象站)
    WeatherStation station;

    // 创建观察者(两个显示设备)
    auto phone1 = std::make_shared<PhoneDisplay>("User's Phone");
    auto phone2 = std::make_shared<PhoneDisplay>("Dad's Phone");
    auto led = std::make_shared<LedDisplay>();

    // 注册观察者
    station.Attach(phone1);
    station.Attach(phone2);
    station.Attach(led);

    // 模拟气象站温度变化,观察者会自动更新
    std::cout << "Setting temperature to 25.5°C...\n";
    station.SetTemperature(25.5);

    std::cout << "\nSetting temperature to 18.2°C...\n";
    station.SetTemperature(18.2);

    // 移除一个观察者
    std::cout << "\nDetaching Dad's Phone...\n";
    station.Detach(phone2);

    std::cout << "Setting temperature to 30.0°C...\n";
    station.SetTemperature(30.0);

    return 0;
}

输出结果:

Setting temperature to 25.5°C...
[User's Phone] Temperature updated: 25.5°C
[Dad's Phone] Temperature updated: 25.5°C
*** LED Display: CURRENT TEMP = 25.5°C ***

Setting temperature to 18.2°C...
[User's Phone] Temperature updated: 18.2°C
[Dad's Phone] Temperature updated: 18.2°C
*** LED Display: CURRENT TEMP = 18.2°C ***

Detaching Dad's Phone...
Setting temperature to 30.0°C...
[User's Phone] Temperature updated: 30°C
*** LED Display: CURRENT TEMP = 30°C ***

更多例子:

示例 1: GUI 事件处理 (按钮点击)

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>

// 前向声明
class Button;

// 观察者接口:事件监听器
class EventListener {
public:
    virtual ~EventListener() = default;
    virtual void onClick(Button& source) = 0;
};

// 主题:按钮
class Button {
    std::vector<EventListener*> listeners_; // 使用原始指针简化示例
    std::string name_;

public:
    Button(const std::string& name) : name_(name) {}

    // 注册观察者
    void addListener(EventListener* listener) {
        listeners_.push_back(listener);
    }

    // 移除观察者
    void removeListener(EventListener* listener) {
        listeners_.erase(std::remove(listeners_.begin(), listeners_.end(), listener), listeners_.end());
    }

    // 模拟用户点击
    void click() {
        std::cout << "Button '" << name_ << "' was clicked!\n";
        notifyListeners();
    }

    const std::string& getName() const { return name_; }

private:
    // 通知所有观察者
    void notifyListeners() {
        for (auto listener : listeners_) {
            listener->onClick(*this);
        }
    }
};

// 具体观察者:登录处理器
class LoginHandler : public EventListener {
public:
    void onClick(Button& source) override {
        std::cout << "[LoginHandler] Handling click from: " << source.getName() << "\n";
        std::cout << "Performing login logic...\n";
    }
};

// 具体观察者:日志记录器
class ClickLogger : public EventListener {
public:
    void onClick(Button& source) override {
        std::cout << "[ClickLogger] Button clicked: " << source.getName() << " at timestamp: 12345\n";
    }
};

int main() {
    Button loginButton("Login");
    
    LoginHandler loginHandler;
    ClickLogger logger;
    
    // 注册监听器
    loginButton.addListener(&loginHandler);
    loginButton.addListener(&logger);
    
    // 模拟点击事件
    loginButton.click();
    
    std::cout << "\nRemoving logger...\n";
    loginButton.removeListener(&logger);
    loginButton.click();
    
    return 0;
}

输出结果:

Button 'Login' was clicked!
[LoginHandler] Handling click from: Login
Performing login logic...
[ClickLogger] Button clicked: Login at timestamp: 12345

Removing logger...
Button 'Login' was clicked!
[LoginHandler] Handling click from: Login
Performing login logic...

示例 2: 发布-订阅系统 (简单的消息主题)

#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>

// 消息类
struct Message {
    std::string topic;
    std::string content;
};

// 订阅者接口
class Subscriber {
public:
    virtual ~Subscriber() = default;
    virtual void onMessage(const Message& msg) = 0;
};

// 消息代理(主题)
class MessageBroker {
    std::map<std::string, std::vector<Subscriber*>> topicSubscribers_;

public:
    // 订阅主题
    void subscribe(const std::string& topic, Subscriber* sub) {
        topicSubscribers_[topic].push_back(sub);
    }

    // 取消订阅
    void unsubscribe(const std::string& topic, Subscriber* sub) {
        auto& subs = topicSubscribers_[topic];
        subs.erase(std::remove(subs.begin(), subs.end(), sub), subs.end());
    }

    // 发布消息
    void publish(const Message& msg) {
        auto it = topicSubscribers_.find(msg.topic);
        if (it != topicSubscribers_.end()) {
            for (auto sub : it->second) {
                sub->onMessage(msg);
            }
        }
    }
};

// 具体订阅者:日志服务
class LogService : public Subscriber {
public:
    void onMessage(const Message& msg) override {
        std::cout << "[LogService] Received on topic '" << msg.topic << "': " << msg.content << "\n";
    }
};

// 具体订阅者:告警服务
class AlertService : public Subscriber {
public:
    void onMessage(const Message& msg) override {
        if (msg.topic == "alerts") {
            std::cout << "[AlertService] ALERT! " << msg.content << "\n";
        }
    }
};

int main() {
    MessageBroker broker;
    
    LogService logger;
    AlertService alerter;
    
    // 订阅主题
    broker.subscribe("logs", &logger);
    broker.subscribe("alerts", &logger);
    broker.subscribe("alerts", &alerter);
    
    // 发布消息
    broker.publish({"logs", "System started successfully"});
    broker.publish({"alerts", "CPU usage over 90%"});
    broker.publish({"metrics", "This will be ignored by all"}); // 无订阅者
    
    return 0;
}

输出结果:

[LogService] Received on topic 'logs': System started successfully
[LogService] Received on topic 'alerts': CPU usage over 90%
[AlertService] ALERT! CPU usage over 90%


网站公告

今日签到

点亮在社区的每一天
去签到