什么是观察者模式?当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象(广播通知)。观察者模式属于行为型模式。
原文地址:(原创) C++11 改进我们的模式之改进观察者模式
我对文章的格式和错别字进行了调整,并在他的基础上把重点部分进一步解释完善。 以下是正文。
正文
和单例模式面临的是同样的问题,主题更新的接口难以统一,很难做出一个通用的观察者模式,还是用到 可变参数模板 解决这个问题,其次还用到了右值引用,避免多余的内存移动。C++11 版本的观察者模式支持注册的观察者为 函数、函数对象 和 lambda 表达式,也避免了虚函数调用,更简洁更通用。
第一版本
直接看代码:
template <typename Func>
class Events
{
public:
Events() : m_nextKey(0)
{
}
~Events() {}
int Connect(Func &&f)
{
return Assgin(f);
}
int Connect(Func &f)
{
return Assgin(f);
}
/// @note 实际注册观察函数
template <typename F>
int Assgin(F &&f)
{
int k = m_nextKey++;
m_connections[k] = f;
return k;
}
/// @note 注销
void Disconnect(int key)
{
m_connections.erase(key);
}
/// @note 通知(执行)观察函数
template <typename... Args>
void Notify(Args &&...args)
{
for (auto &it : m_connections)
{
it.second(std::forward<Args>(args)...);
}
}
private:
int m_nextKey;
std::map<int, Func> m_connections; ///< 保存注册的观察函数
};
测试代码
struct stA
{
int a, b;
};
/// @note 测试观察函数
void print(int a, int b) { cout << a << ", " << b << endl; }
void TestObserver()
{
Events<std::function<void(int, int)>> myevent;
auto key = myevent.Connect(print); ///< 以函数方式注册观察者
stA t;
auto lambdakey = myevent.Connect([&t](int a, int b)
{
t.a = a;
t.b = b;
}); ///< lambda 注册
int a = 1, b = 2;
myevent.Notify(a, b); ///< 广播所有观察者
myevent.Disconnect(key); ///< 移除观察者
}
第二版本
以下更新,增加了 += 和 -= 运算符重载,使用法和 C# 的 event 的用法接近。
#include <map>
template<typename Func>
class Events : NonCopyable
{
public:
Events() :m_nextKey(0)
{}
~Events(){}
/// ------------ 主要新增 ------------
int operator += (Func&& f)
{
return Connect(std::forward<Func>(f));
}
int operator += (Func& f)
{
return Connect(f);
}
template<typename... Args>
void operator()(Args&&... args)
{
Notify(std::forward<Args>(args)...);
}
Events& operator -= (int key)
{
Disconnect(key);
return *this;
}
/// ------------ 主要新增 ------------
void Clear()
{
m_connections.clear();
}
private:
int Connect(Func&& f)
{
return Assgin(std::forward<Func>(f));
}
int Connect(Func& f)
{
return Assgin(f);
}
void Disconnect(int key)
{
m_connections.erase(key);
}
template<typename... Args>
void Notify(Args&&... args)
{
for (auto& it : m_connections)
{
it.second(std::forward<Args>(args)...);
}
}
template<typename F>
int Assgin(F&& f)
{
int index = m_nextKey++;
Push(index, std::forward<Func>(f));
return index;
}
template<typename F>
void Push(int index, F&& f)
{
m_connections.emplace(index, std::move(f));
}
template<typename F>
void Push(int index, F& f)
{
m_connections.emplace(index, f);
}
private:
int m_nextKey;
std::map<int, Func> m_connections;
};
测试代码
struct stA {
int a;
int b;
void fun(int x, int y)
{
a = x;
b = y;
cout << "a = " << a << " b= " << b << endl;
}
};
void print(int a, int b) { cout << a << ", " << b << endl; }
void TestEvent()
{
using Delegate1 = std::function<void(int, int)>;
using Event1 = Events<Delegate1>;
Event1 evnet1;
stA t;
/// @note 添加委托
auto key1 = evnet1 += &print; ///< 普通函数
auto key2 = evnet1 += [&t](int a, int b){ ///< lambda 表达式
t.a = a;
t.b = b;
cout << "t.a = " << t.a << " t.b = " << t.b << endl;
};
auto key3 = evnet1 += std::bind(&stA::fun, &t, std::placeholders::_1, std::placeholders::_2); ///< 函数对象
/// @note 广播
evnet1(2, 3);
/// @note 移除委托
evnet1 -= key1;
evnet1 -= key2;
/// @note 广播
evnet1(4, 5);
/// @note 清空事件
evnet1.Clear();
/// @note 广播
evnet1(1, 2); ///< 清空了,所以什么都不会输出
}
输出结果
在第一个版本的基础上增加了 += 和 -= 运算符,使用法更接近 C#,这里 += 会返回一个 key,这个 key 用来 -= 删除委托时用到,这种做法不太好,只是一个简单的处理。如果内部用 vector 的话,-= 时,根据 function 去删除指定的委托的话,用法就和 C# 完全一致了,不过,这里遇到的问题是 function 不支持比较操作,导致将 function 存入容器后,后面再根据 function 去删除时就找不到对应的 function 了。还没有足够的时间去研究这个问题,留到后面再想办法解决 function 比较的问题。
如果读者有更好的解决办法不妨提出来,讨论一下。