Skip to content
rhcad edited this page Feb 23, 2012 · 7 revisions

事件驱动机制(Observer)

事件驱动机制的主要用途是在不同模块之间实现松耦合(相互隔离)、将复杂的调用流程分离为多个独立操作片断、实现功能扩展点。

一、事件驱动原理

事件驱动机制的具体例子见 example\observerex

定义事件类型主要是定义唯一的字符串以便区分不同的事件,同时为了在编译阶段识别格式错误,自动定义了内部类型。

注册响应函数是将多个相同格式的响应函数(回调函数)记录下来,同时关联到一个事件类型字符串。

触发事件就是根据事件类型字符串找到对应的响应函数,按注册先后顺序依次调用这些响应函数(同步调用方式),将事件参数传到响应函数的形参。

下面是 x3c 框架中的事件驱动机制原理图,可参考:EventDriven.png on x3c

二、使用方法概述

  1. 在H文件中定义事件类型:
  • 包含 eventobserver.h
  • 使用 X3DEFINE_EVENT_1 等宏定义每个事件类型
  1. 定义和实现响应函数(普通全局函数或类的静态函数)
  • 响应函数的返回值类型和形参列表必须与事件类型的定义一致
  1. 使用 X3_REGISTER_OBSERVER 宏注册响应函数
  • 在模块的入口函数CPP文件中包含 observerimpl.h 文件
  1. 使用辅助类(Fire事件名称)触发事件,指定事件参数
  • 在模块的入口函数CPP文件中包含 observerimpl.h 文件

三、定义事件类型

在一个H文件中可定义多个事件类型,例如 myevents.h

#include <observer/fireevent.h>

// void func(int& result)
X3DEFINE_EVENT_1(EventAdd, int&, "data.cms");

// void func(int& result, int extra)
X3DEFINE_EVENT_2(EventAddExtra, int&, int, "data.cms");

// bool func(int& result)
X3DEFINE_EVENT_1Break(EventBreakDemo, int&, "data.cms");

在该文件中首先包含 fireevent.h,然后使用 X3DEFINE_EVENT_1 等宏定义每一个事件类型,使用格式如下:

X3DEFINE_EVENT_0、_1、_2 对应的响应函数的返回值类型为 void,每个响应函数都将依次调用。

X3DEFINE_EVENT_0(事件名称, 事件名称标识后缀);
X3DEFINE_EVENT_1(事件名称, 形参类型, 事件名称标识后缀);
X3DEFINE_EVENT_2(事件名称, 形参类型1, 形参类型2, 事件名称标识后缀);

X3DEFINE_EVENT_0Break、_1Break、_2Break 的格式与上相同,对应的响应函数的返回值类型为 bool,响应函数返回false时不再分发给后续的响应函数。

事件名称 将用于定义一个内部结构体和事件触发的辅助类(类名以Fire开头),在触发和响应事件时将使用该事件名称;

形参类型 为响应函数的形参类型,最多两个形参,可以使用普通类型(例如char*、int),或引用类型(例如 string&,const MyObj&);

事件名称标识后缀 用于避免不同模块中出现相同的事件名称,内部将事件名称和标识后缀组合形成一个不重复的字符串常量,例如"EventAdd.mypkg.x3"、"EventAdd.data.cms"。

四、注册响应函数

使用静态函数来响应简单事件类型,函数的返回值类型和形参列表必须与事件类型的定义一致。可使用普通全局函数或类的静态函数,例如:

void InsertNodeFunc(int& result) {...}
void MyClass::InsertNode(int& result) {...}

在响应函数所在模块的 x3InitializePlugin() 函数或其他函数中注册这些响应函数到对应的事件类型上。使用 X3_REGISTER_OBSERVER 宏注册响应函数,例如:

#include "myevents.h"

void registerHandlers()
{
    X3_REGISTER_OBSERVER(EventAdd, InsertNodeFunc);
    X3_REGISTER_OBSERVER(EventAdd, &MyClass::InsertNode);
}

在响应函数所在模块的入口函数CPP文件(module.cpp 或 main.cpp,包含了pluginimpl.h或实现了createObject函数) 中,需要包含observerimpl.h文件,在该模块中只能在一个CPP文件中包含observerimpl.h文件: #include <observer/observerimpl.h>

五、触发事件

触发事件时需要指定事件的上下文参数,这些参数将转换为响应函数的形参。使用辅助类(类名以Fire开头,然后是事件名称)触发事件。 然后就可以使用 FireEventAdd(0).fireEvent()FireEventAddExtra(10, 20).fireEvent() 来触发消息。 下面是事件触发的示例:

bool test()
{
    int addvalue = 0;
    if (FireEventAdd(addvalue).fireEvent().param != 310)  // 10 + 100 + 200
        return false;

    if (FireEventBreakDemo(1234).fireEvent().param != 11)  // call OnBreakDemo1 once.
        return false;

    return true;
}
Clone this wiki locally