我在 C++ 中实现了一个简单的观察者模式,如下所示:
template<typename T>
class Observer {
private:
virtual void notify(const T& data) = 0;
public:
virtual ~Observer() = default;
};
template<typename T>
class Subject {
public:
void subscribe(Observer<T> *observer);
void unsubscribe(Observer<T> *observer);
virtual ~Subject() = default;
protected:
void notify(const T& data);
private:
std::list<Observer<T>*> observers;
};
template<typename T>
void Subject<T>::subscribe(Observer<T> *observer) {
this->observers.push_back(observer);
}
template<typename T>
void Subject<T>::unsubscribe(Observer<T> *observer) {
this->observers.remove(observer);
}
template<typename T>
void Subject<T>::notify(const T& data) {
for (const auto &observer: this->observers) {
observer->notify(data);
}
}
它定义了两个类,Observer 和Subject。主题存储观察者列表并实现通知方法,该方法用某种数据通知所有观察者。
现在我实现了一个 Car 类,它继承了两次 subject,一个用于移动“事件”,一个用于转向“事件”:
class Car : public Subject<MoveData>, public Subject<SteerData> {
...
void move() {
const MoveData moveData{};
this->Subject<MoveData>::notify(moveData);
}
void steer() {
const SteerData steerData{};
this->Subject<SteerData>::notify(steerData);
}
...
}
如果我现在创建我的 Car 类的实例并调用 move 函数,那么会发生Subject
我的目标是 MoveData-Subject 的通知函数也使用存储在Subject
更现代的方法是根本不再使用虚拟函数。 C++ 有 std::function 这是一个很好的替代方案,并且不需要您为要实现的每种新类型的观察者定义抽象基类。
在线演示:https://onlinegdb.com/Zy04AVXmQ 示例:
#include <iostream>
#include <functional>
#include <utility>
#include <unordered_map>
template<typename... args_t>
class Registrations
{
class Registration
{
public:
Registration(Registrations& registrations, std::size_t cookie) :
m_cookie{ cookie },
m_registrations{ registrations }
{
}
~Registration()
{
m_registrations.unsubscribe(m_cookie);
}
private:
std::size_t m_cookie;
Registrations& m_registrations;
};
public:
[[nodiscard]] Registration subscribe(std::function<void(args_t...)> handler)
{
static std::size_t cookie{ 0ul };
++cookie;
m_handlers.insert({ cookie,handler });
return Registration{ *this,cookie };
}
void notify(args_t...args)
{
for (auto& [_, handler] : m_handlers)
{
handler(args...);
}
}
private:
void unsubscribe(std::size_t id)
{
m_handlers.erase(id);
}
std::unordered_map<std::size_t, std::function<void(args_t...)>> m_handlers;
};
class Car
{
public:
auto OnSteeringAngleChanged(std::function<void(double)> handler)
{
return m_steeringAngleChanged.subscribe(handler);
}
void Steer(double angle)
{
m_steeringAngleChanged.notify(angle);
}
private:
Registrations<double> m_steeringAngleChanged;
};
int main()
{
Car car;
{
auto subscription = car.OnSteeringAngleChanged([](double angle) { std::cout << angle << "\n"; });
car.Steer(15.0); // handler function will be called, subscription is active.
car.Steer(25.0); // handler function will be called, subscription is active.
car.Steer(35.0); // handler function will be called, subscription is active.
// subscription will go out of scope and the handler will no longer be called
}
// no more subscription so no handler will be called.
car.Steer(45.0);
}