和单例模式面临的是同样的问题,主题更新的接口难以统一,很难做出一个通用的观察者模式,还是用到可变模板参数解决这个问题,其次还用到了右值引用,避免多余的内存移动。c++11版本的观察者模式支持注册的观察者为函数、函数对象和lamda表达式,也避免了虚函数调用,更简洁更通用。直接看代码。

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);
}

template<typename F>
int Assgin(F&& f)
{
int k=m_nextKey++;
m_connections[k]=f;
return k;
}

	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)...);
}
} private:
int m_nextKey;
std::map<int, Func> m_connections;
};

测试代码:

struct stA { int a, b; };
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; });//lamda注册 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()
{}
~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;
auto key1 = evnet1 += &print;
auto key2 = evnet1 += [&t](int a, int b){
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);

  //广播
evnet1(, );

  //移除委托
evnet1 -= key1;
evnet1 -= key2;
evnet1(, );

  //清空事件
evnet1.Clear();
evnet1(, );//清空什么都不会输出
}

输出结果:

  在第一个版本的基础上增加了+=和-=运算符,使用法更接近c#,这里+=会返回一个key,这个key用来-=删除委托时用到,这种做法不太好,只是一个简单的处理。如果内部用vector的话,-=时,根据function去删除指定的委托的话,用法就和c#完全一致了,不过,这里遇到的问题是function不支持比较操作,导致将function存入容器后,后面再根据function去删除时就找不到对应的function了。还没有足够的时间去研究这个问题,留到后面再想办法解决function比较的问题。

如果读者有更好的解决办法不妨提出来,讨论一下。

c++11 boost技术交流群:296561497,欢迎大家来交流技术。

(原创)c++11改进我们的模式之改进观察者模式的更多相关文章

  1. (原创)c++11改进我们的模式之改进访问者模式

    本次讲c++11改进我们的模式之改进访问者模式 访问者模式是GOF23个设计模式中比较复杂的模式之一,但是它的功能也很强大,非常适合稳定的继承层次中对象的访问,可以在不修改被访问对象的情况下,动态添加 ...

  2. (原创)c++11改进我们的模式之改进命令模式

    模式虽然精妙,却难完美,比如观察者模式中观察者生命周期的问题:比如访问者模式中循环依赖的问题等等:其它很多模式也存在这样那样的一些不足之处,如使用场景受限.实现复杂.不够简洁.不够通用等.但我觉得不足 ...

  3. (原创)c++11改进我们的模式之改进单例模式

    我会写关于c++11的一个系列的文章,会讲到如何使用c++11改进我们的程序,本次讲如何改进我们的模式,会讲到如何改进单例模式.观察者模式.访问者模式.工厂模式.命令模式等模式.通过c++11的改进, ...

  4. (原创)c++11改进我们的模式之改进代理模式,实现通用的AOP框架

    c++11 boost技术交流群:296561497,欢迎大家来交流技术. 本次要讲的时候如何改进代理模式,具体来说是动态代理模式,动态代理模式一般实现AOP框架,不懂AOP的童鞋看这里.我前面的博文 ...

  5. (原创)c++11改进我们的模式之改进表驱动模式

    所谓表驱动法(Table-Driven Approach),简单讲是指用查表的方法获取值.表驱动是将一些通过较为复杂逻辑语句来得到数据信息的方式,通过查询表的方式来实现,将数据信息存放在表里.对于消除 ...

  6. C++11在时空性能方面的改进

    C++11在时空性能方面的改进 这篇我们聊聊C++11在时间和空间上的改进点: 主要包括以下方面: 新增的高效容器:array.forward_list以及unordered containers: ...

  7. [原创].NET 分布式架构开发实战五 Framework改进篇

    原文:[原创].NET 分布式架构开发实战五 Framework改进篇 .NET 分布式架构开发实战五 Framework改进篇 前言:本来打算这篇文章来写DAL的重构的,现在计划有点改变.之前的文章 ...

  8. 第11章 享元模式(Flyweight Pattern)

    原文 第11章 享元模式(Flyweight Pattern) 概述:   面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题.但是在某些情况下,对象的数量可能会太多,从而导致了运行时 ...

  9. [Java反射机制]用反射改进简单工厂模式设计

    如果做开发的工作,工厂设计模式大概都已经深入人心了,比较常见的例子就是在代码中实现数据库操作类,考虑到后期可能会有数据库类型变换或者迁移,一般都会对一个数据库的操作类抽象出来一个接口,然后用工厂去获取 ...

随机推荐

  1. 转:场景管理--BSP

    对于一个3D引擎来说,最核心的部分应该算是场景组织(scene graph)了,如果这部分你都没有设计好, 那么就别指望开发一个成熟的3D引擎了.为了开发3d引擎,所以我首先就研究这方面的内容,对一个 ...

  2. mac安装thrift

    一.安装brew包管理工具 不想Ubuntu自带apt,redhat自带yum,mac是不自带包管理工具的.需要自己安装,最常用的是brew,Homebrew简称brew,OSX上的软件包管理工具,在 ...

  3. linux shell 脚本攻略学习2

    1.关于文件描述符和重定向: 文件描述符是与一个打开的文件或数据流相关联的整数.文件描述符0.1以及2是系统预留的. 0——stdin(标准输入) 1——stdout(标准输出) 2——stderr( ...

  4. 用UltraEdit判断打开文件的编码类型 用UltraEdit或notepad记事本查看文件编码格式 用UltraEdit查看当前文件编码

    用UltraEdit查看当前文件编码 想判断文件的编码类型? 用强大的UltraEdit-32软件: UltraEdit-32的状态栏可以显示文件的编码类型,详细情况如下: ANSI/ANSCI--- ...

  5. JAVA删除文件及文件夹

    JAVA在删除文件或文件夹时,在java.io.File类下有个delete的方法,并且可以返回true or false, 用这个方法来删除单个文件时,很好使,但在删除文件夹时,如果文件夹下面有文件 ...

  6. Git 远程仓库(分布式版本控制系统)

    前言 远程仓库是指托管在因特网或其他网络中的你的项目的版本库.你可以有好几个远程仓库,通常有些仓库对你只读,有些则可以读写. 1.查看远程仓库 如果想查看你已经配置的远程仓库服务器,可以运行 git ...

  7. Android Framework中的线程Thread及它的threadLoop方法

    当初跟踪Camera的代码中的时候一直追到了HAL层,而在Framework中的代码看见了许很多多的Thread.它们普遍的特点就是有一个threadLoop方法.依照字面的意思应该是这个线程能够循环 ...

  8. 验证时出错。HRESULT = &#39;8000000A&#39;

    这本来是在VS2005下创建的一下项目,后来改用VS2010的开发环境,.NET Framework的版本号还是使用2.0, 但每次生成之后都会在解决方式的同级文件夹下产生一个名称乱码的文件夹, 攻克 ...

  9. @property与@synthesize的差别

    上一篇文章我有讲到self.与_的差别,往往和这个问题相伴随的是我困惑的问题是"@property与@synthesize的差别" @property的使用方法 @interfac ...

  10. 不常用但很有用的git show 和 git blame

    团队使用git 合作时,可能遇见想要查看一段比较难以阅读代码, 此时可能需要联系最新的修改者是哪位,这时候最有用的最快捷的方法就是git blame 啦, 这个指令的output是一个文件的各个区域段 ...