"优雅"的C++观察者模式实现

C++ 是一种强类型的编程语言,于是最近一直在头疼如何实现一个比较优雅的观察者模式。

如果一个事件附带的参数不同的时候,是否可以使用统一的界面来注册?比如我定义了一个事件,A,参数是 int,而事件B的参数是 string,如果采用统一的注册接口?最先想到的办法是事件用int 定义,采用 boost::any 来传递参数,而结束函数统一写成 void hand( boost::any a ) 这样的形式,问题是这样的回调函数只能通过文档来约束参数值,很难知道 a 其实是什么类型的。这毕竟没有 void handle( int a ) 这样直观。

还有个方案是给每个事件写一个注册函数,但是太累人了,如果事件比较多,代码会很乱,事件的定义和对象写在一起,也导致代码而无法重用,耦合太高。

最近灵感一闪,于是有下面这个自我感觉比较优雅的东东:

/* 这是一个观察者模式的简化实现,有助于代码的解耦。
  * 你可以预先定义一些事件,事件的定义使用OBSERVER_EVENT宏:
  * OBSERVER_EVENT( Name, <Params> )
  *  Name 是事件的名称
  *  Params 是参数表
  * 比如
  * OBSERVER_EVENT( MyEvent, int, std::string, long )
  * 
  * 有必要的时候,就可以将一个回调函数绑定到这个事件(订阅)
  * observer a;
  * a.subscribe<Name>( Handle );
  *  Name 是事件的名称
  *  Handle 是回调函数,它的参数,应该和事件的参数表匹配。
  * 当然也可以撤销订阅
  * a.unsubscribe<Name>();
  *  
  * 当事件发生时,可以通过 observer 对象来发送事件
  * a.shot<Name>( <Params> );
  * 
  * 这个对象可以作为基类使用,以帮助对象解耦,这样设计的优点在于,事件的定义、回调的参数表
  * 必须严格匹配,否则就会发生编译错误,以防止代码错误。
  * 特别的,参数可以定义为引用类型,以便让回调函数可以修改它,这时发送事件时,要注意使用 ref() 来包装参数。
 */

当然,库里用了 大量宏和类型推导,代码比较难以阅读,一般人就别去看了。

但是我的原则是,库的代码再乱都没关系,但是接口、使用一定要简单。所以这个只有一个文件的库,使用上还是相当简单的:

这里采用类作为事件,实现都是编译器的,你的代码对不对,编译一下就行了。

你很难写出错误的代码,比如回调函数的参数写错了,编译就无法通过。

 

贴一个完整的测试程序:

#include "observer.hpp"

/// 事件定义
OBSERVER_EVENT(protocol_data_event, std::string, int )

void handle( const std::string& v, int& x )
{
	std::cout << v << " " << x <<  std::endl;
	x=20;
}

int main(int argc, char* argv[])
{
	using namespace lugce;
	observer a;
	a.subscribe<protocol_data_event>( &handle );
	int i=15;
	a.shot<protocol_data_event>("hello", boost::ref(i) );
	return 0;
}


 

库的代码可以在这里找到: 点击打开链接
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值