%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
%%
# 观察者模式(Observer)
「观察者模式」定义了对象间的**一对多关系**,当**一个对象的状态发生改变**时,**所有依赖于它的对象**都会**自动收到通知并==执行特定的更新行为==**。
#### 观察者模式的构成
- **主题**(Subject):接口/抽象类,**==维护一组观察者==**,提供 **==添加、删除==观察者的方法** 以及 "**==通知所有观察者==** " 的方法——会**在==自身状态改变==时通知所有观察者**。
- **观察者**(Observer):接口/抽象类,定义了一个统一的**更新接口**,用于 "**==接收通知==**"
- **具体主题**(Concrete Subject):**实现了主题接口**的 "**具体主题类**",在状态发生变化时通知所有观察者。
- **具体观察者**(Concrete Observer):**实现了观察者接口**的 "**具体观察者类**",定义 **==收到通知后的具体更新行为==**。
> [!summary]
>
> - "**抽象观察者**" 提供一个统一的接口 `update`,该接口供 "**主题**" 调用,**仅用于 "==通知=="**,而不是 "**推送状态**" 。
> - "**具体观察者**" 则实现 `update` 接口,定义 "**观察者自身在收到通知后的具体更新行为**", **根据自身所需来调用 "主题" 提供的接口,向主题拉取最新状态**。
> - 其需持有其**所==注册==的 "具体主题类**" 的**引用**,以便将来从该 "**具体主题类**" 中 **==注销==**。
>
> >
> - "**抽象主题**" 定义用于**通知**、**注册和移除**观察者的**统一接口**
> - "**具体主题**" 实现相关接口,维护一组 **==观察者列表==**,在 "**主题自身状态发生改变**" 时,调用`notify()`通知所有观察者 ——**逐个调用已注册观察者的 `update` 接口**
>
![[_attachment/02-开发笔记/16-设计模式/0-MOC-设计模式.assets/IMG-0-MOC-设计模式-9F0D67E88C8BFBF2462AA8D1A1B18596.png|885]]
# 优缺点
- 优点:**可扩展性强**,无需修改主题代码即**可引入新的观察者类**,**主题类同理**。
- 缺点:未保证订阅者的通知顺序
<br><br>
# 关于观察者模式下的数据传递 🚨
当 "主题" 的状态变更后,其**状态数据如何传递给 "观察者"**?
如果 **`update()` 采用固定数量&类型的参数**,则意味着**所有观察者的 `update()` 接口格式都统一**,**强耦合**。
为此,在实现上有以下两种处理方式:
- (1)引入 "**==抽象状态类==**" 与 "**==具体状态类==**" ,用于**封装状态数据**。
- **==抽象观察者类==** 的 `update()` 接口**传递一个 "==抽象状态类==" 的指针**,而不同的 "**具体主题**" 与 "**具体观察者**" 之间基于 "**==多态==**" 来实际传递 "**具体状态类**",
由不同的具体状态类来封装不同形式的数据。
- (2)**`update` 不带任何参数**,主题类调用 **==`update()` 仅用于 "通知"==**,不传递任何数据。
- **主题类**提供 **"获取其各项状态数据" 的接口**
- **观察者类**收到通知后,**根据自身所需**向主题类 **==拉取对应数据==**
- ~~缺点~~:观察者类需要持有一个对 "主题类" 的引用,即**观察者将被与一个特定主题类绑定**。
# 示例场景
> [!example] 示例一
>
> GUI 框架中的 "**按钮**" :按钮是 "主题",事件类是 "观察者";当点击按钮时,**触发所有注册的响应事件**。
<br><br>
# 示例代码
#### 实现一
```cpp
#include <memory>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
/* 观察者模式实现一: 调用`update()`时传递一个抽象状态类(运行时多态), 实现主题状态的传递. */
// 抽象观察者类
class Observer {
public:
virtual void update() = 0; // 纯虚函数
virtual ~Observer() = default; // 虚析构函数
};
// 抽象主题类
class Subject {
public:
virtual void attach(std::shared_ptr<Observer> observer) = 0;
virtual void detach(std::shared_ptr<Observer> observer) = 0;
virtual void notify() = 0;
virtual ~Subject() = default;
};
// 具体主题类
class ConcreteSubject : public Subject {
private:
std::vector<std::shared_ptr<Observer>> observers_; // 观察者列表(以shared_ptr形式持有)
int state1; // 主题的状态
double state2;
std::string state3;
public:
void attach(std::shared_ptr<Observer> observer) override {
observers_.push_back(observer);
std::cout << "attach observer successfully\n";
};
void detach(std::shared_ptr<Observer> observer) override {
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), observers_.end());
std::cout << "detach observer successfully\n";
};
void notify() override {
// 通知所有观察者
for (const auto& observer : observers_) {
observer->update();
// 观察者提供的update()仅供主题用于"通知"观察者, 而不是推送状态.
// 在观察者收到通知后, 由观察者主动向主题请求拉取其所需的相关状态.
}
};
void stateChanged() { // 多一层封装和抽象(可选), 可包含更多响应逻辑
notify(); // 通知所有观察者
// ...其他响应逻辑
}
void setState(int s1, double s2, std::string s3) {
state1 = s1;
state2 = s2;
state3 = s3;
stateChanged(); // 主题状态发生变更, 触发响应逻辑
}
// 主题提供获取各项状态的接口, 供观察者主动拉取
int getState1() const {
return state1;
}
double getState2() const {
return state2;
}
std::string getState3() const {
return state3;
}
};
// 具体观察者类A
class ConcreteObserverA : public Observer {
private:
std::string name;
int observerState;
std::weak_ptr<ConcreteSubject> subject; // 持有某一具体主题对象的weak_ptr指针. 避免循环引用
public:
ConcreteObserverA(std::string n, std::shared_ptr<ConcreteSubject> s)
: name(n), subject(s), observerState(0) {}
void update() override {
// 观察者收到通知后, 主动向主题请求拉取状态
if (auto s_ptr = subject.lock()) { // 检查主题是否还存在
observerState = s_ptr->getState1(); // 从主题拉取状态
std::cout << "ObserverA " << name << " new state: " << observerState << std::endl;
} else {
std::cout << "ObserverA " << name << " subject is invalid" << std::endl;
}
}
};
// 具体观察者类B
class ConcreteObserverB : public Observer {
private:
std::string name;
double observerState2;
std::string observerState3;
std::weak_ptr<ConcreteSubject> subject; // 持有某一具体主题对象的weak_ptr指针. 避免循环引用
public:
ConcreteObserverB(std::string n, std::shared_ptr<ConcreteSubject> s)
: name(n), subject(s), observerState2(0.0), observerState3("") {}
void update() override {
if (auto s_ptr = subject.lock()) { // 检查主题是否还存在
observerState2 = s_ptr->getState2(); // 从主题拉取状态
observerState3 = s_ptr->getState3(); // 从主题拉取状态
std::cout << "ObserverB " << name
<< " new state2: " << observerState2
<< " new state3: " << observerState3 << std::endl;
} else {
std::cout << "ObserverB " << name << " subject is invalid" << std::endl;
}
}
};
int main() {
// 声明一个具体主题对象
auto subject = std::make_shared<ConcreteSubject>();
// Ps: 如果希望在实例化具体观察者时, 就将其注册到主题, 可以利用"工厂模式"来实现.
// 声明多个不同类型的具体观察者对象
// A1, A2声明时提供主题对象指针, 注册到该主题
auto observerA1 = std::make_shared<ConcreteObserverA>("A1", subject);
auto observerA2 = std::make_shared<ConcreteObserverA>("A2", subject);
auto observerB1 = std::make_shared<ConcreteObserverB>("B1", subject);
auto observerB2 = std::make_shared<ConcreteObserverB>("B2", subject);
// 将A1, B1注册到主题
subject->attach(observerA1);
subject->attach(observerB1);
// 变更主题状态, 主题自动通知所有观察者
subject->setState(1, 0.2, "T1");
subject->setState(2, 3.4, "T2");
// 将A2, B2也注册到主题
subject->attach(observerA2);
subject->attach(observerB2);
subject->setState(4, 2.4, "T3");
// 注销观察者
subject->detach(observerA1);
subject->detach(observerA2);
subject->detach(observerB1);
subject->detach(observerB1);
}
```
#### 实现二
```cpp
#include <memory>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
/* 观察者模式实现二: 调用`update()`时传递一个抽象状态类(运行时多态), 实现主题状态的传递. */
// 抽象状态类
struct State {
public:
virtual ~State() = default;
};
// 具体状态类A
struct ConcreteStateA : public State {
public:
int state;
};
// 具体状态类B
struct ConcreteStateB : public State {
public:
double state1;
std::string state2;
};
// 抽象观察者类
class Observer {
public:
virtual void update(std::shared_ptr<State> state) = 0; // 纯虚函数
virtual ~Observer() = default; // 虚析构函数
};
// 抽象主题类
class Subject {
public:
virtual void attach(std::shared_ptr<Observer> observer) = 0;
virtual void detach(std::shared_ptr<Observer> observer) = 0;
virtual void notify() = 0;
virtual ~Subject() = default;
};
// 具体主题类A
class ConcreteSubjectA : public Subject {
private:
std::vector<std::shared_ptr<Observer>> observers_; // 观察者列表(以shared_ptr形式持有)
ConcreteStateA stateA; // 主题的状态
public:
void attach(std::shared_ptr<Observer> observer) override {
observers_.push_back(observer);
std::cout << "attach observer successfully\n";
};
void detach(std::shared_ptr<Observer> observer) override {
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), observers_.end());
std::cout << "detach observer successfully\n";
};
void notify() override {
// 通知所有观察者(传递状态)
std::shared_ptr<State> ptr_s = std::make_shared<ConcreteStateA>(stateA); // 多态
for (const auto& observer : observers_) {
observer->update(ptr_s);
}
};
void stateChanged() { // 多一层封装和抽象(可选), 可包含更多响应逻辑
notify(); // 通知所有观察者
// ...其他响应逻辑
}
void setState(int s1) {
stateA.state = s1;
stateChanged(); // 主题状态发生变更, 触发响应逻辑
}
};
// 具体主题类B
class ConcreteSubjectB : public Subject {
private:
std::vector<std::shared_ptr<Observer>> observers_; // 观察者列表(以shared_ptr形式持有)
ConcreteStateB stateB; // 主题的状态
public:
void attach(std::shared_ptr<Observer> observer) override {
observers_.push_back(observer);
std::cout << "attach observer successfully\n";
};
void detach(std::shared_ptr<Observer> observer) override {
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), observers_.end());
std::cout << "detach observer successfully\n";
};
void notify() override {
// 通知所有观察者(传递状态)
std::shared_ptr<State> ptr_s = std::make_shared<ConcreteStateB>(stateB); // 多态
for (const auto& observer : observers_) {
observer->update(ptr_s);
}
};
void stateChanged() { // 多一层封装和抽象(可选), 可包含更多响应逻辑
notify(); // 通知所有观察者
// ...其他响应逻辑
}
void setState(double s1, std::string s2) {
stateB.state1 = s1;
stateB.state2 = s2;
stateChanged(); // 主题状态发生变更, 触发响应逻辑
}
};
// 具体观察者类A
class ConcreteObserverA : public Observer {
private:
std::string name;
int observerState;
std::weak_ptr<ConcreteSubjectA> subject; // 持有某一具体主题对象的weak_ptr指针. 避免循环引用
public:
ConcreteObserverA(std::string n, std::shared_ptr<ConcreteSubjectA> s)
: name(n), subject(s), observerState(0) {}
void update(std::shared_ptr<State> ptr_s) override {
// 向下转型: 将shared_ptr<State>转为shared_ptr<ConcreteStateA>
if (auto ptr = std::dynamic_pointer_cast<ConcreteStateA>(ptr_s)) {
observerState = ptr->state; // 从主题拉取状态
std::cout << "ObserverA " << name << " new state: " << observerState << std::endl;
} else {
std::cout << "ObserverA " << name << " subject is invalid" << std::endl;
}
}
};
// 具体观察者类B
class ConcreteObserverB : public Observer {
private:
std::string name;
double observerState2;
std::string observerState3;
std::weak_ptr<ConcreteSubjectB> subject; // 持有某一具体主题对象的weak_ptr指针. 避免循环引用
public:
ConcreteObserverB(std::string n, std::shared_ptr<ConcreteSubjectB> s)
: name(n), subject(s), observerState2(0.0), observerState3("") {}
void update(std::shared_ptr<State> ptr_s) override {
// 向下转型: 将shared_ptr<State>转为shared_ptr<ConcreteStateB>
if (auto ptr = std::dynamic_pointer_cast<ConcreteStateB>(ptr_s)) {
observerState2 = ptr->state1; // 从主题拉取状态
observerState3 = ptr->state2; // 从主题拉取状态
std::cout << "ObserverB " << name
<< " new state2: " << observerState2
<< " new state3: " << observerState3 << std::endl;
} else {
std::cout << "ObserverB " << name << " subject is invalid" << std::endl;
}
}
};
int main() {
// 声明一个具体主题对象
auto subjectA = std::make_shared<ConcreteSubjectA>();
auto subjectB = std::make_shared<ConcreteSubjectB>();
// Ps: 如果希望在实例化具体观察者时, 就将其注册到主题, 可以利用"工厂模式"来实现.
// 声明多个不同类型的具体观察者对象
// A1, A2声明时提供主题对象指针, 注册到该主题
auto observerA1 = std::make_shared<ConcreteObserverA>("A1", subjectA);
auto observerA2 = std::make_shared<ConcreteObserverA>("A2", subjectA);
auto observerB1 = std::make_shared<ConcreteObserverB>("B1", subjectB);
auto observerB2 = std::make_shared<ConcreteObserverB>("B2", subjectB);
// 将A1, B1注册到主题
subjectA->attach(observerA1);
subjectB->attach(observerB1);
// 变更主题状态, 主题自动通知所有观察者
subjectA->setState(1);
subjectB->setState(3.4, "T2");
// 将A2, B2也注册到主题
subjectA->attach(observerA2);
subjectB->attach(observerB2);
subjectA->setState(3);
subjectB->setState(2.4, "T3");
// 注销观察者
subjectA->detach(observerA1);
subjectA->detach(observerA2);
subjectB->detach(observerB1);
subjectB->detach(observerB1);
}
```
<br><br>
# 参考资料
# Footnotes