设计模式 — 观察者模式
观察者模式(Observer Pattern)是一种行为设计模式,它允许对象(称为“观察者”或“订阅者”)在另一个对象(称为“主题”或“发布者”)的状态发生变化时得到通知并自动更新。观察者模式的核心思想是解耦主题与观察者之间的依赖关系,使得主题不需要知道具体的观察者对象,只需维护一个观察者列表即可。
1. 观察者模式的组成
1.1 主题 subject(发布者)
主题的构成部件主要有一下几个:
- 维护一个观察者列表(vector<std::shared_ptr>);
- 提供订阅和取消订阅接口;
- 提供用于通知订阅者信息的接口。
注意:可总结为:一个列表,两个接口,一个通知
1.2 观察者 observer(订阅者)
订阅者主要由以下几个模块构成:
- 定义更新接口(update(…));
- 实现具体业务的更新逻辑。
2. 具体实例实现
#include <iostream>
#include <vector>
#include <memory>
// 观察者接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(int value) = 0;
};
// 主题接口
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(int value) {
for (const auto& observer : observers) {
observer->update(value);
}
}
private:
std::vector<std::shared_ptr<Observer>> observers;
};
// 具体的观察者类
class ConcreteObserverA : public Observer {
public:
void update(int value) override {
std::cout << "ConcreteObserverA: Value updated to " << value << std::endl;
}
};
class ConcreteObserverB : public Observer {
public:
void update(int value) override {
std::cout << "ConcreteObserverB: Value updated to " << value << std::endl;
}
};
// 具体的主题类
class ConcreteSubject : public Subject {
public:
void setValue(int value) {
this->value = value;
notify(value);
}
private:
int value;
};
int main() {
// 创建主题
ConcreteSubject subject;
// 创建观察者
auto observerA = std::make_shared<ConcreteObserverA>();
auto observerB = std::make_shared<ConcreteObserverB>();
// 注册观察者
subject.attach(observerA);
subject.attach(observerB);
// 调试信息,检查观察者列表
std::cout << "After attaching observers, number of observers: " << subject.observers.size() << std::endl;
// 更改主题的值,观察者会收到通知
subject.setValue(10);
// 注销一个观察者
subject.detach(observerA);
// 调试信息,检查观察者列表
std::cout << "After detaching observerA, number of observers: " << subject.observers.size() << std::endl;
// 再次更改主题的值,只有 observerB 会收到通知
subject.setValue(20);
return 0;
}
3. 观察者模式的优缺点
3.1 优点
- 解耦:主题和观察者之间是松耦合的,主题不需要知道具体的观察者类型,只需要维护一个观察者列表。
- 扩展性:可以轻松地增加或删除观察者,而不需要修改主题的代码。
- 灵活性:观察者可以根据自己的需求定义不同的更新行为。
3.2 缺点
- 复杂性:如果主题有大量的观察者,通知可能变得复杂且耗时;
- 内存管理:要小心管理观察者的生命周期,防止内存泄漏;
- 线程安全性:订阅&取消订阅存在线程安全问题,因为vector本身非线程安全,且erase、remove的安全性也仅仅在单线程中才成立,故此环节应注意线程安全问题。
4. 观察者模式的相关思考
观察者模式虽然为设计提供了便利,但是并不是完美地,同时也有几个很有趣的思考供大家把玩:
- 如果同一个订阅者(观察者)被主题添加多次,会出现什么问题?
- 如果允许拷贝Observer,那么,detach函数会将每一个实例都删除么?
- 如果把上述的列表换成其他容器,上述行为会有什么影响,比如std::set/boost::unordered_set以阻止复制行为,这对普通操作有什么影响?
- 如果需要将所有观察者按优先级排序,应该如何操作?
总之,目前还没有一个理想的观察者模式的实现可以满足所有需求,无论选择那种实现方式,都会有一些妥协。