简介:观察者模式是一种行为模式,定义对象间一对多依赖关系,使得当一个对象状态改变时,所有依赖者都会得到通知。在C++中,观察者模式可通过“拉”模式实现,即观察者主动获取状态信息。文章详细介绍了观察者模式的结构和在C++中的实现过程,并通过示例展示了如何在实际应用中使用该模式。
1. 观察者模式概念和在软件设计中的作用
在软件工程领域,设计模式是一种被广泛认可和使用的成功解决方案的模板。它们是根据多年的经验总结出来的,可以解决特定软件设计问题。在这众多设计模式中,观察者模式(Observer Pattern)扮演了至关重要的角色。
1.1 模式简介
1.1.1 设计模式的定义
设计模式提供了一种标准化的方式以描述和实现软件组件之间的关系和交互。它们在代码复用、系统的可维护性和可扩展性方面有巨大的贡献。
1.1.2 观察者模式的起源和基本概念
观察者模式是一种行为设计模式,允许对象之间一个对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。这种模式最早被广泛应用于GUI编程,现在它已经成为许多软件框架和库中的核心组件。
1.2 观察者模式的作用
1.2.1 解耦合的设计原则
观察者模式通过引入中介角色来减少系统组件之间的耦合度。这样,一个主题(被观察者)就可以独立于其观察者而改变,而观察者也可以独立于主题。
1.2.2 观察者模式在软件开发中的重要性
在软件开发中,观察者模式提供了一种灵活、强大的机制,用于实现解耦合、动态绑定以及在特定事件发生时更新多个对象的功能。这种模式在事件驱动系统、模型-视图-控制器架构(MVC)中尤其重要。
总的来说,观察者模式让系统组件之间更容易通信,同时保持了组件的独立性,是现代软件设计中的一个基石。
2. 观察者模式在C++中的“拉”式实现方式
在软件开发过程中,观察者模式是一种广泛使用的模式,它用于建立对象间的一对多依赖关系,当一个对象状态发生改变时,所有依赖的对象都会得到通知并自动更新。在C++中,“拉”式实现方式是一种实现观察者模式的方法,它让观察者通过主动查询的方式来获取被观察者的状态变化信息,这与“推”式实现方式中的被动接收消息有所不同。本章将深入探讨“拉”式实现的基本原理,并详细说明实现过程中的关键步骤。
2.1 “拉”式实现的基本原理
2.1.1 “拉”式与“推”式实现的区别
在“拉”式实现中,观察者需要主动去询问被观察者是否有更新。当事件发生时,被观察者通知观察者,但并不直接传递数据,而是由观察者在接收到通知后,主动从被观察者获取最新的状态信息。这种方式的优点在于它允许观察者根据自己的需要和时机去获取数据,从而减少不必要的数据传递和处理。
相比之下,“推”式实现则是被观察者主动将状态变化的信息推送给所有的观察者。这种方法可能在某些情况下效率较低,因为不管观察者是否需要,都会接收到数据推送。
2.1.2 “拉”式实现的代码结构和逻辑
为了实现“拉”式观察者模式,我们首先需要定义出观察者(Observer)和主题(Subject)的接口。然后,具体主题(ConcreteSubject)类需要管理观察者列表,并提供状态更新的方法。具体观察者(ConcreteObserver)类则实现观察者的接口,当接收到更新通知时,它们将调用主题提供的方法来获取最新状态。
下面是这一部分的代码结构和逻辑的概括性描述:
class Observer {
public:
virtual void update() = 0; // 观察者的更新方法,由具体观察者实现
};
class Subject {
private:
list<Observer*> observers; // 存储观察者的列表
public:
void attach(Observer* o) { observers.push_back(o); } // 添加观察者
void detach(Observer* o) { observers.remove(o); } // 移除观察者
virtual void notify() { // 通知所有观察者
for (auto& observer : observers) {
observer->update();
}
}
};
class ConcreteSubject : public Subject {
private:
int state; // 主题的状态
public:
int getState() const { return state; } // 获取当前状态的方法供观察者使用
void setState(int newState) { // 状态改变时调用
state = newState;
notify(); // 通知所有观察者状态已改变
}
};
class ConcreteObserver : public Observer {
private:
ConcreteSubject* subject;
public:
void update() override {
if (subject) {
int newState = subject->getState(); // 拉取最新的状态
// 根据状态变化进行相应操作...
}
}
void setSubject(ConcreteSubject* s) { subject = s; }
};
2.2 “拉”式实现的关键步骤
2.2.1 观察者接口的设计
在设计观察者接口时,一个关键的方法是 update() 。此方法定义了观察者在接收到状态更新通知时应执行的操作。设计时要确保 update() 方法足够通用,以便适用于不同类型的通知和更新策略。
2.2.2 具体主题与具体观察者的实现
具体主题类 ConcreteSubject 继承自抽象主题类 Subject 并实现了状态的改变方法 setState() 。每当状态发生变化时, setState() 方法将被调用,并随后通知所有已经注册的观察者。
具体观察者类 ConcreteObserver 实现了观察者的接口,即 update() 方法。在这个方法中,观察者将调用主题提供的方法来获取最新的状态信息。
2.2.3 事件触发和状态更新机制
在“拉”式实现中,事件触发和状态更新机制是通过 ConcreteSubject 类中的 setState() 方法实现的。当状态发生改变时, setState() 被调用,并随后调用 notify() 方法通知所有观察者。观察者在收到通知后,会在 update() 方法中调用 getState() 来获取最新的状态信息。
这个机制的设计需要仔细考虑如何高效地更新状态和通知观察者,同时避免过多的更新或通知造成不必要的资源消耗。
2.2.4 代码实现和逻辑分析
下面是一个简单的代码实现例子,它展示了如何在C++中实现“拉”式观察者模式:
#include <iostream>
#include <list>
#include <memory>
// 观察者接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update() = 0;
};
// 主题接口
class Subject {
protected:
std::list<std::shared_ptr<Observer>> observers;
public:
void attach(std::shared_ptr<Observer> o) { observers.push_back(o); }
void detach(std::shared_ptr<Observer> o) {
observers.remove(o);
}
void notify() {
for (auto& observer : observers) {
observer->update();
}
}
};
// 具体主题类
class ConcreteSubject : public Subject {
private:
int state;
public:
int getState() const { return state; }
void setState(int newState) {
state = newState;
notify();
}
};
// 具体观察者类
class ConcreteObserver : public Observer {
private:
std::weak_ptr<Subject> subject;
public:
ConcreteObserver(std::shared_ptr<Subject> s) : subject(s) { }
void update() override {
auto s = subject.lock();
if (s) {
int newState = s->getState();
std::cout << "Observer: I've seen the new state " << newState << std::endl;
}
}
};
// 主函数,演示了观察者模式的使用
int main() {
auto subject = std::make_shared<ConcreteSubject>();
auto observer1 = std::make_shared<ConcreteObserver>(subject);
auto observer2 = std::make_shared<ConcreteObserver>(subject);
subject->attach(observer1);
subject->attach(observer2);
std::cout << "First state change: " << std::endl;
subject->setState(1);
subject->detach(observer1);
std::cout << "\nSecond state change: " << std::endl;
subject->setState(2);
return 0;
}
在上述代码中,我们首先定义了 Observer 和 Subject 接口,然后是 ConcreteSubject 和 ConcreteObserver 的具体实现。在 main 函数中,我们创建了一个 ConcreteSubject 实例和两个 ConcreteObserver 实例,并将它们分别附加到主题上。通过改变状态并调用 notify() ,我们展示了如何通知观察者对象,并让他们根据主题状态的变化执行相应的更新。
通过代码逻辑,我们观察到在“拉”式实现中,观察者在接收到通知后,会主动调用主题的状态获取方法。这确保了观察者只在它们需要时获取更新,这在许多应用场合中是非常高效的。
参数说明和执行逻辑:
-
Subject类维护了一个观察者列表observers,这是一个std::list,用于存储所有注册的观察者。 -
Subject类提供了attach和detach方法,用于管理观察者列表。 -
ConcreteSubject类继承了Subject并实现了setState()方法。在setState()中,除了更新状态,还会调用notify()通知所有观察者。 -
ConcreteObserver类实现了一个构造函数,该构造函数接受一个Subject的std::shared_ptr。观察者使用这个智能指针在update()方法中安全地访问Subject的状态。
代码中通过 std::shared_ptr 和 std::weak_ptr 的使用,展示了如何管理对象的生命周期和避免循环引用的问题。通过 main 函数中的示例,我们可以看到如何实际使用观察者模式来响应主题状态的变化。
3. 观察者模式的关键结构:Subject(主题)、Observer(观察者)、ConcreteSubject(具体主题)、ConcreteObserver(具体观察者)
3.1 主题和观察者的设计原则
3.1.1 Subject(主题)的设计要点
在观察者模式中, Subject 是一个核心概念,它负责维护观察者列表并提供注册、删除和通知观察者的方法。设计良好的 Subject 应具备以下要点:
- 状态管理 :
Subject应独立管理自己的状态。这样,当状态发生变化时,无需任何观察者介入即可更新。 - 观察者列表 :应有一个明确的数据结构来存储所有注册的观察者,通常是使用容器类如
std::vector或std::list。 - 通知机制 :需要一个机制在适当的时候通知所有观察者,通知通常发生在
Subject状态发生变化时。 - 线程安全 :如果应用程序是多线程的,
Subject应提供线程安全的方式来管理其状态以及通知观察者。
3.1.2 Observer(观察者)的设计要点
Observer 接口则定义了观察者必须实现的方法,这些方法被 Subject 调用来通知观察者状态变更。其设计要点包括:
- 接口通用性 :
Observer应提供一个清晰和通用的接口,使得任何ConcreteObserver都可以被Subject所使用。 - 灵活的通知机制 :观察者应能适应不同的通知方式,无论是同步还是异步,即时还是延迟。
- 与
Subject的解耦 :尽管Observer需要注册到Subject并响应其通知,但是它们应尽量减少直接依赖以维持低耦合度。 - 状态更新逻辑 :
Observer需要维护自己的状态,并定义更新状态的具体逻辑。
3.2 具体主题和观察者的实现
3.2.1 ConcreteSubject的具体实现
ConcreteSubject 类继承自 Subject ,它实现具体的状态管理逻辑,包括状态更新和状态查询。下面是具体实现的关键步骤:
- 状态定义和更新 :在
ConcreteSubject中,定义实际的状态数据成员,并在状态变更时更新这些数据。 - 维护观察者列表 :实现注册和删除观察者的逻辑,确保在任何时候,
ConcreteSubject都能持有当前活跃的观察者列表。 - 状态变更通知 :当状态发生变化时,遍历观察者列表,对每一个观察者发出通知。
以下是 ConcreteSubject 的一个简单实现代码示例:
#include <iostream>
#include <vector>
class Observer; // 前置声明
class ConcreteSubject {
private:
int state;
std::vector<Observer*> observers;
public:
void registerObserver(Observer* o) {
observers.push_back(o);
}
void removeObserver(Observer* o) {
observers.erase(std::remove(observers.begin(), observers.end(), o), observers.end());
}
void setState(int newState) {
state = newState;
notify();
}
int getState() const {
return state;
}
void notify() {
for (auto observer : observers) {
observer->update();
}
}
};
在这个示例中, ConcreteSubject 维护了一个 state 成员变量,它通过 setState 方法来更新状态。同时, notify 方法会遍历注册的观察者列表并调用它们的 update 方法。
3.2.2 ConcreteObserver的具体实现
ConcreteObserver 类继承自 Observer ,实现具体的观察逻辑和状态更新。
- 观察者状态维护 :与
ConcreteSubject相同,ConcreteObserver也会维护其自身的状态。 - 响应通知 :当接收到
ConcreteSubject发来的通知时,ConcreteObserver应更新自己的状态以反映Subject的变化。 - 实现更新方法 :实现
Observer接口中的update方法,这个方法包含具体的状态更新逻辑。
以下是一个 ConcreteObserver 的示例代码:
class ConcreteSubject; // 前置声明
class ConcreteObserver : public Observer {
private:
int observerState;
ConcreteSubject* subject;
public:
ConcreteObserver(ConcreteSubject* s) : subject(s), observerState(0) {
subject->registerObserver(this);
}
~ConcreteObserver() {
subject->removeObserver(this);
}
void update() override {
observerState = subject->getState();
performUpdate();
}
void performUpdate() {
// 具体的更新逻辑
std::cout << "ConcreteObserver state updated to " << observerState << std::endl;
}
};
在这个示例中, ConcreteObserver 维护了一个 observerState 成员变量,这个状态会在 update 方法被调用时被更新。
3.2.3 两者交互的协作机制
ConcreteSubject 和 ConcreteObserver 通过维护彼此的引用和状态更新方法来实现协作。 Subject 的状态变化通过 setState 方法实现,然后 Subject 遍历它的观察者列表并通知每一个观察者。观察者在收到通知时,通过实现的 update 方法来更新自己的状态。
这种协作机制遵循了观察者模式的核心原则:松耦合。 Subject 不需要知道 Observer 的具体实现细节,反之亦然。两者之间的通信仅通过接口方法来实现。
上图展示了 ConcreteSubject 和 ConcreteObserver 之间的协作机制。当 ConcreteSubject 的状态发生变化时,它通知所有的 ConcreteObserver 实例。这些观察者随后根据需要更新自己的状态,以保持与主题的同步。
4. C++代码示例,展示了观察者模式的具体实现和应用
4.1 实现观察者模式的C++代码分析
4.1.1 观察者接口和主题接口的C++定义
#include <iostream>
#include <list>
#include <memory>
// Observer interface
class Observer {
public:
virtual void update(float temp, float humidity, float pressure) = 0;
};
// Subject interface
class Subject {
public:
virtual void registerObserver(std::shared_ptr<Observer> o) = 0;
virtual void removeObserver(std::shared_ptr<Observer> o) = 0;
virtual void notifyObservers() = 0;
};
在上述代码中,我们定义了两个接口: Observer 和 Subject 。 Observer 接口包含一个 update 方法,用于在主题状态改变时更新观察者的状态。 Subject 接口定义了注册、移除观察者的方法以及通知所有注册观察者的方法。
4.1.2 具体主题和具体观察者的C++实现
// ConcreteSubject implementation
class WeatherData : public Subject {
private:
std::list<std::shared_ptr<Observer>> observers;
float temperature;
float humidity;
float pressure;
public:
void measurementsChanged() {
notifyObservers();
}
void setMeasurements(float temperature, float humidity, float pressure) {
this->temperature = temperature;
this->humidity = humidity;
this->pressure = pressure;
measurementsChanged();
}
void registerObserver(std::shared_ptr<Observer> o) override {
observers.push_back(o);
}
void removeObserver(std::shared_ptr<Observer> o) override {
observers.remove(o);
}
void notifyObservers() override {
for (auto& observer : observers) {
observer->update(temperature, humidity, pressure);
}
}
};
在 WeatherData 类中,我们实现了 Subject 接口,管理一个观察者列表,并在测量数据改变时调用 notifyObservers 方法通知所有观察者。
// ConcreteObserver implementation
class CurrentConditionsDisplay : public Observer {
private:
float temperature;
float humidity;
std::shared_ptr<WeatherData> weatherData;
public:
CurrentConditionsDisplay(std::shared_ptr<WeatherData> weatherData) {
this->weatherData = weatherData;
weatherData->registerObserver(shared_from_this());
}
void update(float temp, float humidity, float pressure) override {
this->temperature = temp;
this->humidity = humidity;
display();
}
void display() {
std::cout << "Current conditions: " << temperature << "F degrees and " << humidity << "% humidity" << std::endl;
}
};
CurrentConditionsDisplay 类实现了 Observer 接口,并在 update 方法中接收来自 WeatherData 的数据更新,并调用 display 方法展示当前的天气情况。
4.2 应用观察者模式的实例
4.2.1 实例的业务逻辑背景
在这个例子中,我们可以想象一个气象站,它需要展示当前的天气情况。通过应用观察者模式,当气象站的温度、湿度或气压发生变化时,相关的显示设备(观察者)可以自动更新其展示内容。
4.2.2 实例中观察者模式的应用展示
int main() {
std::shared_ptr<WeatherData> weatherData = std::make_shared<WeatherData>();
std::shared_ptr<CurrentConditionsDisplay> currentDisplay = std::make_shared<CurrentConditionsDisplay>(weatherData);
weatherData->setMeasurements(80, 65, 30.4);
weatherData->setMeasurements(82, 70, 29.2);
weatherData->setMeasurements(78, 90, 29.2);
return 0;
}
在 main 函数中,我们创建了一个 WeatherData 对象,并且一个 CurrentConditionsDisplay 观察者被注册到气象数据对象上。当气象数据更新时, WeatherData 对象会通知 CurrentConditionsDisplay ,后者展示更新后的天气信息。
以上代码示例展示了观察者模式在C++中的具体实现。通过定义接口和具体类,以及如何注册和通知观察者,我们创建了一个简单但功能强大的系统来管理天气信息的展示。这个模式的应用不限于天气展示,也适用于其他需要异步通知场景的软件系统设计。
5. 实现过程包括定义Observer接口、Subject接口,以及实现具体主题和观察者类
在本章中,我们将深入探讨如何在C++中实现观察者模式。实现过程涉及定义Observer和Subject接口,以及具体主题和观察者类的实现。我们还将讨论在实现过程中需要考虑的优化策略,比如性能、系统扩展性、维护性以及线程安全。
5.1 Observer和Subject接口的定义
首先,我们需要定义Observer和Subject接口,它们是观察者模式的核心组件。
5.1.1 Observer接口的方法声明
Observer接口定义了那些需要响应Subject状态变化的对象所必须实现的方法。一个典型的Observer接口可能包含如下方法:
class Observer {
public:
virtual void update(Subject *subject) = 0; // 更新操作由主题调用
};
5.1.2 Subject接口的方法声明
Subject接口定义了操作Observer的接口,包括添加、删除以及通知所有已注册的Observer对象的方法:
class Subject {
public:
virtual void attach(Observer *observer) = 0; // 将观察者对象附加到主题
virtual void detach(Observer *observer) = 0; // 从主题中分离观察者对象
virtual void notify() = 0; // 通知所有观察者对象
};
5.2 具体类的实现细节
接下来,我们将看到具体类的实现细节,包括具体观察者类和具体主题类。
5.2.1 具体观察者类的实现
具体观察者类实现了Observer接口,并提供了具体更新逻辑:
class ConcreteObserver : public Observer {
private:
std::string observerState; // 观察者状态
public:
void update(Subject *subject) override {
// 实现根据主题状态变化更新观察者状态的逻辑
ConcreteSubject *cSubject = static_cast<ConcreteSubject *>(subject);
observerState = cSubject->getState();
// 其他基于更新状态的操作
}
// 其他观察者特定的方法
};
5.2.2 具体主题类的实现
具体主题类实现了Subject接口,并维护了观察者的列表:
class ConcreteSubject : public Subject {
private:
std::string subjectState; // 主题状态
std::vector<Observer*> observers; // 观察者列表
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(auto observer : observers) {
observer->update(this);
}
}
void setState(const std::string& newState) {
subjectState = newState;
notify(); // 状态变化后通知所有观察者
}
std::string getState() const {
return subjectState;
}
// 其他主题特定的方法
};
5.2.3 状态更新和事件通知的逻辑
在上述代码中,ConcreteSubject类的 setState 方法允许更改主题的状态,并通过调用 notify 方法来通知所有已注册的观察者。每个观察者将调用其 update 方法,该方法定义了观察者应如何响应状态变化。
5.3 实现考量与优化策略
5.3.1 性能考量
在实现观察者模式时,需要考虑性能影响。尤其是当Subject需要通知大量Observer时,可以考虑使用更高效的通知机制,比如批量更新,或者异步通知。
5.3.2 系统扩展性和维护性的考量
观察者模式提供了良好的系统扩展性,新类型的Observer和Subject可以很容易地加入到系统中,而不会影响现有的对象。设计时要确保遵循开闭原则,即类应该对扩展开放,对修改封闭。
5.3.3 线程安全和同步机制
如果Subject和Observer分布在不同的线程中,则需要考虑线程安全。在多线程环境中,需要通过锁或其他同步机制来保证Subject在通知Observer时不会出现竞态条件。可以使用互斥锁(mutexes)或信号量(semaphores)来控制对共享数据的访问。
在这一章节中,我们探讨了如何在C++中实现观察者模式,从定义Observer和Subject接口开始,到具体类的实现和应用考量。观察者模式是软件工程中的重要模式之一,它在解耦和事件驱动架构中扮演着核心角色,希望本章节能帮助你更深入地理解并应用观察者模式。
简介:观察者模式是一种行为模式,定义对象间一对多依赖关系,使得当一个对象状态改变时,所有依赖者都会得到通知。在C++中,观察者模式可通过“拉”模式实现,即观察者主动获取状态信息。文章详细介绍了观察者模式的结构和在C++中的实现过程,并通过示例展示了如何在实际应用中使用该模式。
1534

被折叠的 条评论
为什么被折叠?



