这个示例在 In-Depth C 11 书中有很详细的说明和实现,这里只记录自己关心的部分。传统观察者模式基本上都是要提供一个接口类用于提供观察者继承从而在数据变化时让接口被调用。一个典型的例子如下:
代码语言:javascript复制class Subject;
class Observer
{
public:
virtual ~Observer() {}
virtual void Update(Subject* theChangedSubject) = 0;
protected:
Observer() {}
private:
};
class Subject
{
public:
virtual ~Subject() {}
virtual void Attach(Observer* observer)
{
observers_.emplace_back(observer);
}
virtual void Detach(Observer* observer)
{
observers_.remove(observer);
}
virtual void Notify()
{
for (auto& iter : observers_)
{
iter->Update(this);
}
}
protected:
Subject() {}
private:
std::list<Observer*> observers_;
};
以上示例有诸多问题,比如如果想让被通知的接口参数化怎么办?是不是所有观察者都要是 Observer 的派生类?主要就是需要继承这种强关系和参数不够灵活。使用 C 11 可变参数模板来解决传参问题,使用 std::function 来解决继承问题。
代码语言:javascript复制// Observer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <functional>
#include <iostream>
#include <list>
#include <map>
class NonCopyable
{
protected:
NonCopyable() = default;
~NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable operator=(const NonCopyable&) = delete;
};
template<typename F>
class Events : public NonCopyable
{
public:
Events() {}
~Events() {}
// 注册观察者,支持右值引用
int Connect(F&& f)
{
return Assign(f);
}
// 普通注册观察者函数
int Connect(const F& f)
{
return Assign(f);
}
// 移除观察者
void Disconnect(int key)
{
connections_.erase(key);
}
// 通知
template<typename... Args>
void Notify(Args&&... args)
{
for (auto& iter : connections_)
{
iter.second(std::forward<Args>(args)...);
}
}
private:
template<typename F>
int Assign(F& f)
{
int k = observer_ ;
connections_.emplace(k, std::forward<F>(f));
return k;
}
private:
int observer_ = 0;
std::map<int, F> connections_;
};
struct T
{
int a, b;
void print(int a, int b) { std::cout << a << ", " << b << std::endl; }
};
void print(int a, int b) { std::cout << a << ", " << b << std::endl; }
int main()
{
Events<std::function<void(int, int)>> event;
// 第一种注册方式
auto key = event.Connect(print);
// 第二种 lambda 方式
T t;
auto lambdaKey = event.Connect([&t](int a, int b) {
t.a = a; t.b = b;
std::cout << t.a << ", " << t.b << std::endl;
});
// 第三种 bind 一个 function 的方式
auto functionKey = event.Connect(std::bind(&T::print, &t, std::placeholders::_1, std::placeholders::_2));
int a = 10, b = 20;
event.Notify(a, b);
}