设计模式:观察者模式
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式的核心组成
-
Subject(主题/被观察者):
-
维护一个观察者列表
-
提供添加、删除观察者的接口
-
提供通知观察者的接口
-
-
Observer(观察者):
-
定义一个更新接口,用于在主题状态改变时接收通知
-
观察者模式的实现方式
1. 经典实现(使用抽象基类)
#include <iostream>
#include <vector>
#include <algorithm>
// 前向声明
class Observer;
// 主题接口
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
virtual ~Subject() = default;
};
// 观察者接口
class Observer {
public:
virtual void update(Subject* subject) = 0;
virtual ~Observer() = default;
};
// 具体主题
class ConcreteSubject : public Subject {
private:
std::vector<Observer*> observers;
int state;
public:
void attach(Observer* observer) override {
observers.push_back(observer);
}
void detach(Observer* observer) override {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notify() override {
for (Observer* observer : observers) {
observer->update(this);
}
}
int getState() const {
return state;
}
void setState(int newState) {
state = newState;
notify(); // 状态改变时自动通知观察者
}
};
// 具体观察者A
class ConcreteObserverA : public Observer {
public:
void update(Subject* subject) override {
if (auto concreteSubject = dynamic_cast<ConcreteSubject*>(subject)) {
std::cout << "ConcreteObserverA: Subject state changed to "
<< concreteSubject->getState() << std::endl;
}
}
};
// 具体观察者B
class ConcreteObserverB : public Observer {
public:
void update(Subject* subject) override {
if (auto concreteSubject = dynamic_cast<ConcreteSubject*>(subject)) {
std::cout << "ConcreteObserverB: Subject state changed to "
<< concreteSubject->getState() << std::endl;
}
}
};
// 使用示例
int main() {
ConcreteSubject subject;
ConcreteObserverA observerA;
ConcreteObserverB observerB;
subject.attach(&observerA);
subject.attach(&observerB);
subject.setState(10); // 两个观察者都会收到通知
subject.setState(20);
subject.detach(&observerB);
subject.setState(30); // 只有observerA会收到通知
return 0;
}
2. 使用现代C++(智能指针、lambda等)
#include <iostream>
#include <vector>
#include <memory>
#include <functional>
class Subject {
private:
std::vector<std::function<void(int)>> observers;
int state;
public:
void attach(const std::function<void(int)>& observer) {
observers.push_back(observer);
}
void notify() {
for (const auto& observer : observers) {
observer(state);
}
}
void setState(int newState) {
state = newState;
notify();
}
};
int main() {
Subject subject;
// 使用lambda表达式作为观察者
auto observer1 = [](int state) {
std::cout << "Observer1: State changed to " << state << std::endl;
};
auto observer2 = [](int state) {
std::cout << "Observer2: State changed to " << state << std::endl;
};
subject.attach(observer1);
subject.attach(observer2);
subject.setState(10);
subject.setState(20);
return 0;
}
3. 使用信号槽机制(类似Qt的信号槽)
#include <iostream>
#include <vector>
#include <functional>
#include <memory>
template <typename... Args>
class Signal {
private:
std::vector<std::function<void(Args...)>> slots;
public:
void connect(const std::function<void(Args...)>& slot) {
slots.push_back(slot);
}
void emit(Args... args) {
for (auto& slot : slots) {
slot(args...);
}
}
};
class Model {
public:
Signal<int> dataChanged;
void setData(int newData) {
// 数据改变时发射信号
dataChanged.emit(newData);
}
};
class View {
public:
void update(int data) {
std::cout << "View updated with data: " << data << std::endl;
}
};
int main() {
Model model;
View view;
// 连接信号和槽
model.dataChanged.connect([&view](int data) { view.update(data); });
model.setData(10);
model.setData(20);
return 0;
}
观察者模式 UML 类图
+-------------------+ +-------------------+
| Subject | | Observer |
+-------------------+ +-------------------+
| -observers: List |<>------->| +update(): void |
+-------------------+ +-------------------+
| +attach(Observer) | ^
| +detach(Observer) | |
| +notify(): void | +-------------------+
+-------------------+ | ConcreteObserver |
^ +-------------------+
| | +update(): void |
+-------------------+ +-------------------+
| ConcreteSubject |
+-------------------+
| -state: int |
+-------------------+
| +getState(): int |
| +setState(): void |
+-------------------+
UML 元素解析
1. 核心角色
| 类/接口 | 作用 |
|---|---|
Subject |
抽象主题(被观察者),维护观察者列表,提供 attach/detach/notify 接口 |
ConcreteSubject |
具体主题,维护状态(state),状态变化时调用 notify() 通知观察者 |
Observer |
抽象观察者,定义 update() 接口 |
ConcreteObserver |
具体观察者,实现 update() 以响应主题状态变化 |
2. 关系说明
| 关系类型 | 描述 |
|---|---|
| 关联关系 | Subject 聚合 Observer(用菱形箭头表示,主题持有观察者列表) |
| 泛化关系 | ConcreteSubject 继承 Subject,ConcreteObserver 继承 Observer |
| 依赖关系 | Observer::update() 依赖 Subject 作为参数(虚线箭头,可选绘制) |
时序图示例
ConcreteSubject ConcreteObserver
| |
| setState(10) |
|--------------------->| notify()
| |
| | update()
|<---------------------|
| |
-
主题调用
setState()修改状态。 -
主题内部调用
notify()遍历观察者列表。 -
每个观察者执行
update()响应变化。
关键设计点
-
松耦合
-
主题无需知道观察者的具体类,只需调用
update()接口。 -
新增观察者无需修改主题代码(符合开闭原则)。
-
-
推模型 vs 拉模型
-
推模型:主题将状态通过
update()参数传递给观察者(如update(int state))。 -
拉模型:观察者从主题主动获取状态(如
update()内调用subject.getState())。
-
-
线程安全
-
多线程环境下需对观察者列表的修改(
attach/detach)和遍历(notify)加锁。
-
代码映射 UML
// Subject(抽象主题)
class Subject {
protected:
std::vector<Observer*> observers; // 聚合 Observer
public:
virtual void attach(Observer* obs) = 0;
virtual void notify() = 0;
};
// ConcreteSubject(具体主题)
class WeatherData : public Subject {
private:
int temperature; // 状态
public:
void setTemperature(int temp) {
temperature = temp;
notify(); // 状态变化时通知观察者
}
};
// Observer(抽象观察者)
class Observer {
public:
virtual void update(Subject* sub) = 0; // 依赖 Subject
};
// ConcreteObserver(具体观察者)
class Display : public Observer {
public:
void update(Subject* sub) override {
if (auto weather = dynamic_cast<WeatherData*>(sub)) {
std::cout << "Temperature: " << weather->getTemperature();
}
}
};
常见变体
-
事件总线(Event Bus)
-
主题扩展为全局事件中心,支持多类型事件订阅/发布。
-
-
信号槽(Signal-Slot)
-
如 Qt 框架,用
connect()绑定信号发射器和槽函数。
-
观察者模式的优缺点
优点:
-
支持简单的广播通信,自动通知所有已订阅的对象
-
主题和观察者之间松耦合,主题不需要知道具体的观察者类
-
可以在运行时动态添加和删除观察者
-
符合开放-封闭原则,可以引入新的观察者而不修改主题
缺点:
-
通知顺序不可控
-
如果观察者处理不当,可能导致循环引用
-
观察者过多时,通知可能花费较长时间
-
观察者不知道彼此的存在,可能导致更新冲突
观察者模式的适用场景
-
当一个对象的状态改变需要通知其他多个对象时
-
当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时
-
需要在运行时动态建立和解除对象间关系时
-
需要实现类似广播机制的功能时
实际应用示例
-
GUI事件处理(如按钮点击事件)
-
发布-订阅系统
-
模型-视图-控制器(MVC)架构中的模型和视图
-
股票价格变动通知
-
游戏中的事件系统
注意事项
-
内存管理:注意避免循环引用,特别是在使用shared_ptr时
-
线程安全:在多线程环境下使用时需要确保线程安全
-
性能考虑:当观察者数量很大时,通知操作可能成为性能瓶颈
-
更新顺序:观察者的更新顺序通常是不确定的,不要依赖特定的顺序
观察者模式是C++中非常有用且常见的设计模式,通过解耦主题和观察者,非常适合实现事件驱动架构和用户界面开发中。现代C++的特性(如lambda表达式、智能指针等)使得实现观察者模式更加简洁和安全。
更多推荐

所有评论(0)