观察者模式

最后修改:

观察者模式(Observer Pattern) 是一种行为型设计模式,它定义了对象之间 一对多的依赖关系,当一个对象(称为“主题”或“被观察者”)的状态发生改变时,所有依赖于它的对象(称为“观察者”)都会自动收到通知并更新。

🎯 核心思想

  • 解耦:被观察者和观察者之间松耦合,彼此不知道对方的具体实现。
  • 自动通知:被观察者状态变化时,自动广播通知给所有注册的观察者。
  • 动态注册/注销:观察者可以动态地添加或移除。

🧱 角色组成

角色说明
Subject(主题 / 被观察者)它保存着被观察的状态。它维护一个观察者列表,并提供用于订阅、取消订阅和通知观察者的方法
Observer(观察者)定义接收更新的接口,通知观察者主题的状态变化
ConcreteSubject(具体主题)具体的被观察者,状态变化时通知所有观察者
ConcreteObserver(具体观察者)实现更新逻辑,对接收到的通知做出反应

🌐 实际应用场景

场景说明
GUI 事件系统按钮点击、鼠标移动等事件通知
MVC 架构View 观察 Model 的变化
消息队列 / 发布-订阅系统如 Kafka、Redis Pub/Sub
游戏开发
日志监控日志生成时通知色状态变化通知 UI 或 AI多个处理模块
股票价格监控股价变动通知多个投资者对象

✅ 优点

优点说明
松耦合被观察者和观察者之间无直接依赖
可扩展性可以动态添加/删除观察者
支持广播通信一个主题可通知多个观察者
符合开闭原则扩展新观察者无需修改主题

❌ 缺点

缺点说明
性能问题观察者过多时,通知可能耗时
内存泄漏风险若未正确注销,观察者可能无法被释放(尤其在使用裸指针时)
通知顺序不确定通常不保证通知顺序
循环依赖观察者反过来影响被观察者可能导致无限循环

🆚 与发布-订阅模式(Pub-Sub)的区别

特性观察者模式发布-订阅模式
耦合度紧耦合(直接引用)松耦合(通过消息中间件)
通信方式同步调用通常异步
中间件有(如消息队列)
适用范围同一进程内可跨进程、跨网络

🔍 简单说:观察者模式是“直接通知”,发布-订阅是“通过邮局中转”。

✅ 总结

观察者模式是一种强大且常用的设计模式,特别适合:

  • 需要 状态变化通知 的场景
  • 实现 事件驱动架构
  • 构建 响应式系统

实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>
#include <string>

using namespace std;

class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const string& newMessage) = 0;
};

class Subject {
public:
    virtual ~Subject() = default;
    virtual void sub_register(std::shared_ptr<Observer> observer) = 0;  //由于register为关键字,所以函数
    virtual void sub_unregister(const string& name, std::shared_ptr<Observer> observer) = 0; //为了注销提示名字,增加了name参数
    virtual void notify() = 0;
};

class GroupChat : public Subject {
private:
    vector<std::shared_ptr<Observer>> observers;
    vector<string> messages;
public:
    void sub_register(std::shared_ptr<Observer> observer) override {
        observers.push_back(observer);
    }

    void sub_unregister(const string& name, std::shared_ptr<Observer> observer) override {
        observers.erase(
            std::remove(observers.begin(), observers.end(), observer),
            observers.end()
        );
        std::cout << name << " Observer unregistered.\n";
    }

    void notify() override {
        for (auto& observer : observers) {
            observer->update(messages.back());
        }
    }

    void sendMessage(const string& msg) {
        messages.push_back(msg);
        notify();
    }
};

class Friend : public Observer {
private:
    string name;
public:
    Friend(const string& n) : name(n) {}

    void update(const string& newMessage) override {
        cout << name << " received a message: " << newMessage << endl;
    }

    string get_name() {
        return name;
    }
};

int main() {
    auto chat = std::make_shared<GroupChat>();
    auto friend1 = std::make_shared<Friend>("Alice");
    auto friend2 = std::make_shared<Friend>("Bob");
    auto friend3 = std::make_shared<Friend>("Charlie");

    chat->sub_register(friend1);
    chat->sub_register(friend2);
    chat->sub_register(friend3);

    chat->sendMessage("Hey everyone, let's plan our trip!");
    

    chat->sub_unregister(friend1->get_name(), friend1);
    chat->sendMessage("Alice get out!");

    chat->sub_unregister(friend2->get_name(), friend2);
    chat->sendMessage("Bob get out!");

    chat->sub_unregister(friend3->get_name(), friend3);
    chat->sendMessage("Charlie get out!");

    return 0;
}

Refernece

https://www.geeksforgeeks.org/system-design/observer-pattern-c-design-patterns/ https://refactoring.guru/design-patterns/observer/cpp/example https://www.bogotobogo.com/DesignPatterns/observer.php https://medium.com/@lokeshbihani99/observer-pattern-in-c-366a1e9226f6 https://github.com/gayashanbc/observer-pattern-cpp

山重水复疑无路,柳暗花明又一村
使用 Hugo 构建
主题 StackJimmy 设计