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

  1. template<typename Func>
  2. class Events
  3. {
  4. public:
  5. Events():m_nextKey(0)
  6. {}
  7. ~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;
}

  1. void Disconnect(int key)
         {
  2. m_connections.erase(key);
  3. }
  4.  
  5. template<typename... Args>
  6. void Notify(Args&&... args)
  7. {
  8. for (auto& it: m_connections)
  9. {
  10. it.second(std::forward<Args>(args)...);
  11. }
  12. }
  13.  
  14. private:
  15. int m_nextKey;
  16. std::map<int, Func> m_connections;
  17. };

测试代码:

  1. struct stA { int a, b; };
  2. void print(int a, int b) { cout << a << ", " << b << endl; }
  3. void TestObserver()
  4. {
  5. Events<std::function<void(int,int)>> myevent;
  6.  
  7. auto key=myevent.Connect(print); //以函数方式注册观察者
  8. stA t;
  9. auto lambdakey=myevent.Connect([&t](int a, int b){ t.a=a; t.b=b; });//lamda注册
  10.  
  11. int a=1,b=2;
  12. myevent.Notify(a,b); //广播所有观察者
  13. myevent.Disconnect(key); //移除观察者
  14.  
  15. }

/**********************更新,增加+=和-=运算符重载,使用法和c#的event的用法接近。

  1. #include <map>
  2. template<typename Func>
  3. class Events : NonCopyable
  4. {
  5. public:
  6. Events() :m_nextKey()
  7. {}
  8. ~Events(){}
  9.  
  10. int operator += (Func&& f)
  11. {
  12. return Connect(std::forward<Func>(f));
  13. }
  14.  
  15. int operator += (Func& f)
  16. {
  17. return Connect(f);
  18. }
  19.  
  20. template<typename... Args>
  21. void operator()(Args&&... args)
  22. {
  23. Notify(std::forward<Args>(args)...);
  24. }
  25.  
  26. Events& operator -= (int key)
  27. {
  28. Disconnect(key);
  29. return *this;
  30. }
  31.  
  32. void Clear()
  33. {
  34. m_connections.clear();
  35. }
  36.  
  37. private:
  38. int Connect(Func&& f)
  39. {
  40. return Assgin(std::forward<Func>(f));
  41. }
  42.  
  43. int Connect(Func& f)
  44. {
  45. return Assgin(f);
  46. }
  47.  
  48. void Disconnect(int key)
  49. {
  50. m_connections.erase(key);
  51. }
  52.  
  53. template<typename... Args>
  54. void Notify(Args&&... args)
  55. {
  56. for (auto& it : m_connections)
  57. {
  58. it.second(std::forward<Args>(args)...);
  59. }
  60. }
  61.  
  62. template<typename F>
  63. int Assgin(F&& f)
  64. {
  65. int index = m_nextKey++;
  66. Push(index, std::forward<Func>(f));
  67. return index;
  68. }
  69.  
  70. template<typename F>
  71. void Push(int index, F&& f)
  72. {
  73. m_connections.emplace(index, std::move(f));
  74. }
  75.  
  76. template<typename F>
  77. void Push(int index, F& f)
  78. {
  79. m_connections.emplace(index, f);
  80. }
  81.  
  82. private:
  83. int m_nextKey;
  84. std::map<int, Func> m_connections;
  85. };

测试代码:

  1. struct stA {
  2. int a;
  3. int b;
  4. void fun(int x, int y)
  5. {
  6. a = x;
  7. b = y;
  8. cout << "a = " << a << " b= " << b << endl;
  9. }
  10. };
  11. void print(int a, int b) { cout << a << ", " << b << endl; }
  12. void TestEvent()
  13. {
  14. using Delegate1 = std::function<void(int, int)>;
  15. using Event1 = Events<Delegate1>;
  16. Event1 evnet1;
      //添加委托
  17. stA t;
  18. auto key1 = evnet1 += &print;
  19. auto key2 = evnet1 += [&t](int a, int b){
  20. t.a = a;
  21. t.b = b;
  22. cout << "t.a = " << t.a << " t.b = " << t.b << endl;
  23. };
  24. auto key3 = evnet1 += std::bind(&stA::fun, &t, std::placeholders::_1, std::placeholders::_2);

  25.   //广播
  26. evnet1(, );

  27.   //移除委托
  28. evnet1 -= key1;
  29. evnet1 -= key2;
  30. evnet1(, );

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

输出结果:

  在第一个版本的基础上增加了+=和-=运算符,使用法更接近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. 网页屏蔽Backspace事件,输入框不屏蔽

    document.onkeydown = function (e) { var code; if (!e){ var e = window.event;} if (e.keyCode){ code = ...

  2. (原)使用1080Ti显卡时安装ubuntu16.04.1及驱动的步骤

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/6811328.html 参考网址: http://www.cnblogs.com/darkknightz ...

  3. Mybaits 使用多数据库源错误 --MapperScannerConfigurer配置

    我在配置文件里面配置了一个数据源,数据源参数是根据配置文件加载.数据源在mybaits和自定义数据工具类中使用,但是启动工程后,报错如下:  ### Cause: org.springframewor ...

  4. php使用wkhtmltopdf导出pdf

    参考:史上最强php生成pdf文件,html转pdf文件方法 http://biostall.com/wkhtmltopdf-add-header-footer-to-only-first-last- ...

  5. 安卓listView实现下拉刷新上拉加载滑动仿QQ的删除功能

    大家对这些功能都是看的多了,然后对上拉刷新和下拉加载的原理都是非常清楚的,所以实现这功能其实也就是为了让大家能够从众多的同行们来进行比较学习而已,虽然即使是这样,但是面试的时候面试官还是会问你上拉和下 ...

  6. SQL表之间复制数据、选出随机几条数据、删除重复数据、取得自增长列等操作

    --表之间数据复制 SELECT* INTO yozhu FROM yo --复制一份表 SELECT* INTO yozhu1 FROM yo where 1<>1 --只复制表结构,无 ...

  7. 安卓Textview的getLineCount返回0

    ViewTreeObserver observer = tv.getViewTreeObserver(); // textAbstract为TextView控件 observer.addOnGloba ...

  8. Easyui combobox 始终选择第一个的问题

    //必须指定 id 和 text $('#contact_city').combobox({ valueField:'id', textField:'text', });

  9. chrome表单自动填充导致input文本框背景变成偏黄色问题解决

    chrome表单自动填充后,input文本框的背景会变成偏黄色的,想必大家都会碰到这种情况吧, 这是由于chrome会默认给自动填充的input表单加上input:-webkit-autofill私有 ...

  10. Oracle 12C -- in-database archiving

    在同一张表中,通过将row置为inactive状态来实现数据的归档.数据库中,可以对那些inactive row进行压缩优化.在实现归档的同时,应用可以被限制只访问那些active状态的数据.默认情况 ...