%% # 纲要 > 主干纲要、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