C++观察者模式及其实现(附带实例)
观察者模式是一种一对多的依赖关系,当一个对象的状态发生变化时,其依赖的所有对象都会得到通知并自动更新。
观察者模式通常应用于许多现实世界的场景,如事件处理、消息传递、GUI开发等。在软件开发中,观察者模式有着广泛的应用,例如在 MVC(Model-View-Controller)模式中,视图(View)通常充当观察者角色,模型(Model)则是主题,视图监听模型的变化以更新自身的状态。
观察者模式的优点是解耦了目标和观察者,使它们可以独立发展,并且可以灵活地增加、删除观察者对象。同时,观察者模式符合开闭原则,可以在不修改目标对象的情况下增加新的观察者。
观察者模式的缺点主要是,首先观察者模式可能引起性能问题,特别是当观察者较多或者更新操作较复杂时;其次,观察者模式可能导致循环引用,需要避免。

图 1 传统观察者模式UML简图
目标是被观察者的对象,它维护一组观察者对象,并提供方法,用于添加、删除和通知观察者。观察者是定义了一个更新接口的对象,它能够接收目标的通知并相应地进行更新操作。具体观察者是观察者接口的实现类,它实现了更新接口,并定义了具体的更新操作。
为了将不同数量的数据类型打包成一个 std::vector<variant> ,模块中使用模板递归的方式进行打包处理,如下图所示。

图 2 C++11模板观察者模式UML简图
观察者 observer 是一个包含纯虚函数的基类,在实际的观察者中需要实现 update 方法来响应目标数据发生变化后的通知。在 subject 中维护了一个观察者的对象表,当 subject 发生变化后会发出通知。
FOR__ 是一个运行期的元函数,用来将可变的参数转换为 std::vector<variant> 数据,代码如下:
FOR__ 通过递归展开的方式,每次展开处理一种数据类型。将 std::tuple 封装的数据逐个封装成 std::vector<variant>,代码如下:
下面展开终止条件,最后一个展开的 extract() 函数为了兼容整个调用的逻辑关系,虽然定义了函数,但是实际上不需要使用,代码如下:
函数 addObserver() 用来添加观察者对象。removeObserver() 函数用来移除观察者对象,代码如下:
模板函数 notifyObservers() 用来发送变化通知,这种方法使用可变参模板实现以增加通用性。内部通过构造万能数据类型 wheels::variant 来存储实际的数据内容,并将所有的数据构造成向量对象作为参数进行传递,代码如下:
在类 Display 中需要实现虚函数 update(),用来接收事件通知。在 TemperatureSensor 类中,发生事件时需要调用基类中的 notifyObservers() 函数发出通知,代码如下:
在 main() 函数中定义一个被观察对象 sensor 和两个观察者 display1、display2,sensor 类首先调用 addObserver() 添加两个观察者,然后调用 updateTemperature() 函数发送通知,代码如下:
观察者模式通常应用于许多现实世界的场景,如事件处理、消息传递、GUI开发等。在软件开发中,观察者模式有着广泛的应用,例如在 MVC(Model-View-Controller)模式中,视图(View)通常充当观察者角色,模型(Model)则是主题,视图监听模型的变化以更新自身的状态。
观察者模式的优点是解耦了目标和观察者,使它们可以独立发展,并且可以灵活地增加、删除观察者对象。同时,观察者模式符合开闭原则,可以在不修改目标对象的情况下增加新的观察者。
观察者模式的缺点主要是,首先观察者模式可能引起性能问题,特别是当观察者较多或者更新操作较复杂时;其次,观察者模式可能导致循环引用,需要避免。
传统观察者模式
传统观察者模式一般涉及 3 个角色:目标(Subject)、观察者(Observer)和具体观察者(Concrete Observer),如下图所示。
图 1 传统观察者模式UML简图
目标是被观察者的对象,它维护一组观察者对象,并提供方法,用于添加、删除和通知观察者。观察者是定义了一个更新接口的对象,它能够接收目标的通知并相应地进行更新操作。具体观察者是观察者接口的实现类,它实现了更新接口,并定义了具体的更新操作。
C++11元编程下的结构设计
本节的观察者模式模板类通过 variant 万能数据类型存储数据内容,用于通知消息传递。由于需要考虑实际使用的数据数量和类型都会有不同的变化,所以使用可变参的函数模板,用来触发通知操作,将多种不同的参数组包成 std::vector<variant> 的方式来传递参数。为了将不同数量的数据类型打包成一个 std::vector<variant> ,模块中使用模板递归的方式进行打包处理,如下图所示。

图 2 C++11模板观察者模式UML简图
C++观察者模式实现和解析
代码主要分成 3 部分,即观察者 observer、观察目标 subject 和打包器 FOR__。观察者 observer 是一个包含纯虚函数的基类,在实际的观察者中需要实现 update 方法来响应目标数据发生变化后的通知。在 subject 中维护了一个观察者的对象表,当 subject 发生变化后会发出通知。
FOR__ 是一个运行期的元函数,用来将可变的参数转换为 std::vector<variant> 数据,代码如下:
//designM/observer.hpp
class observer{
public:
virtual~observer(){}
virtual void update(const std::vector< wheels::variant >&data) = 0;
//内部使用
virtual bool needRelease(){return false;}
};
FOR__ 通过递归展开的方式,每次展开处理一种数据类型。将 std::tuple 封装的数据逐个封装成 std::vector<variant>,代码如下:
//designM/observer.hpp
template< int N,typename tupleType >
struct FOR__
{
static void extract(std::vector<wheels::variant >¶m,const tupleType&t)
{
param[N] = wheels::variant::make(std::get< N >(t));
FOR__< N - 1,tupleType >::extract(param,t);
}
};
下面展开终止条件,最后一个展开的 extract() 函数为了兼容整个调用的逻辑关系,虽然定义了函数,但是实际上不需要使用,代码如下:
template<typename tupleType>
struct FOR__<0, tupleType> {
static void extract(std::vector<wheels::variant>& param, const tupleType& t) {
(void)param;
(void)t;
}
};
class subject {
public:
using obsvFunc_t = std::function<void(const std::vector<wheels::variant>&)>;
protected:
// 管理以函数对象添加的 observer 对象
class observer__ : public observer {
private:
obsvFunc_t m_func__;
public:
explicit observer__(obsvFunc_t func) : m_func__(func) {}
virtual ~observer__() {}
virtual void update(const std::vector<wheels::variant>& data) final {
m_func__(data);
}
virtual bool needRelease() final { return true; }
};
public:
subject() {}
virtual ~subject() {}
};
函数 addObserver() 用来添加观察者对象。removeObserver() 函数用来移除观察者对象,代码如下:
// designM/observer.hpp
template<typename realType>
void addObserver(realType* obsv)
{
std::unique_lock<std::mutex> lck(m_mutex__);
m_observers__.push_back(obsv);
}
observer* addObserver(obsvFunc_t func)
{
observer* ret = nullptr;
ret = new observer__(func);
{
std::unique_lock<std::mutex> lck(m_mutex__);
m_observers__.push_back(ret);
}
return ret;
}
void removeObserver(observer* obsv)
{
std::unique_lock<std::mutex> lck(m_mutex__);
if (m_observers__.size() == 0) return;
auto it = std::find(m_observers__.begin(),
m_observers__.end(),
obsv);
if (it != m_observers__.end()) {
if ((*it)->needRelease()) {
delete *it;
}
m_observers__.erase(it);
}
}
模板函数 notifyObservers() 用来发送变化通知,这种方法使用可变参模板实现以增加通用性。内部通过构造万能数据类型 wheels::variant 来存储实际的数据内容,并将所有的数据构造成向量对象作为参数进行传递,代码如下:
// designM/observer.hpp
template<typename ...Args>
void notifyObservers(Args&&... args)
{
// 构造 variant 数组
if (sizeof...(args) > 0) {
// 构造 tuple 以方便后续展开
auto t = std::make_tuple(std::forward<Args>(args)...);
// 定义要传递的参数 param
std::vector<wheels::variant> param(sizeof...(args));
// 使用 FoR 模板展开,把 tuple 中的数据提取到 vector 中
FoR__<sizeof...(args) - 1, decltype(t)>::extract(param, t);
// 加锁并通知所有观察者
std::lock_guard<std::mutex> lck(m_mutex__);
for (auto obsv : m_observers__) {
obsv->update(param);
}
} else {
// 当没有参数时使用空的 vector
std::vector<wheels::variant> param;
std::lock_guard<std::mutex> lck(m_mutex__);
for (auto obsv : m_observers__) {
obsv->update(param);
}
}
}
private:
std::mutex m_mutex__;
std::vector<observer*> m_observers__;
};
C++观察者模式应用示例
下面的示例模拟从温度测量到内容显示的过程。类 TemperatureSensor 是温度传感器类,作为被观察对象;类 Display 和类 DisplaySec 是显示器类,作为观察者。在类 Display 中需要实现虚函数 update(),用来接收事件通知。在 TemperatureSensor 类中,发生事件时需要调用基类中的 notifyObservers() 函数发出通知,代码如下:
//observer.cpp
#include <iostream>
#include <string>
#include "designM/observer.hpp"
using namespace wheels;
using namespace dm;
class TemperatureSensor : public subject {
public:
void updateTemperature(int temp) {
notifyObservers(temp);
}
};
class Display : public observer {
public:
Display() {}
virtual void update(const std::vector<wheels::variant>& data) override {
if (data.size() > 0) {
int temperature = data[0].get<int>();
std::cout << "Display 当前温度为 "
<< temperature << "℃" << std::endl;
}
}
private:
TemperatureSensor m_sensor;
};
class DisplaySec : public observer {
public:
DisplaySec() {}
virtual void update(const std::vector<wheels::variant>& data) override {
if (data.size() > 0) {
int temperature = data[0].get<int>();
std::cout << "DisplaySec 当前温度为 "
<< temperature << "℃" << std::endl;
}
}
private:
TemperatureSensor m_sensor;
};
在 main() 函数中定义一个被观察对象 sensor 和两个观察者 display1、display2,sensor 类首先调用 addObserver() 添加两个观察者,然后调用 updateTemperature() 函数发送通知,代码如下:
int main()
{
//定义两个不同的观察者
Display display1;
DisplaySec display2;
TemperatureSensor sensor;
//添加观察者
sensor.addObserver(&display1);
sensor.addObserver(&display2);
//发出通知
sensor.updateTemperature(25);
sensor.updateTemperature(28);
return 0;
}
上述示例的运行结果如下:
Display当前温度为25℃
DisplaySec当前温度为25℃
Display当前温度为28℃
DisplaySec当前温度为28℃
ICP备案:
公安联网备案: