event-based framework 分析

分析一下基于事件的程序架构,程序中所有的对象之间的通信都是通过事件总线,

当一个对象需要执行一个动作时,他向事件总线发射一个事件,然后事件总线根据之前注册的

事件与事件处理函数,找到事件对应的事件处理函数并调用它。

这个是比较简单的同步模式,没有应用多线程,是老外写的一个demo。不过从分析来看,老外写

程序不是简单的面向过程的实现功能,而是像盖大楼一样,有房间,有楼梯,有电梯,有结构。

很有一手。这绝对不是国人能比的。大体看了一下我周围的码农,大部分只是实现规定的功能,

并没有考虑到后期的维护、扩展等。没有应用“设计模式”的概念。

第一个类是所有类的基类:


#ifndef_SRC_EVENT_OBJECT_HPP_

#define_SRC_EVENT_OBJECT_HPP_

/**

* \brief Root classof the type hierarchy

*

* All events andevent handlers derive from this class

*/

class Object {

public:

/**

* \brief Defaultempty constructor

*/

Object() { }


/**

* Empty virtualdestructor

*/

virtual ~Object(){ }


/**

* Default emptycopy constructor

* @param other Theinstance to copy from

*/

Object (constObject& other) { }

};

#endif /*_SRC_EVENT_OBJECT_HPP_ */

第二个类,是所有Event的基类,表面上看来面,这么多深层次的基类没有什么使用,其实不然,

这为以后的扩展留下了很大的空间。留着有好处的。

#ifndef_SRC_EVENT_EVENT_HPP_

#define_SRC_EVENT_EVENT_HPP_

#include"Object.hpp"

#include <typeindex>

#include <typeinfo>

#include <vector>

#include <stdexcept>


/**

* \brief The baseevent class, all events inherit from this class

*/

class Event : publicObject

{

public:

/**

* \brief Defaultconstructor

*

* @param typeIndexThe type ID of the inherited class

* @param senderThe sender of the event

*/

Event(Object &sender) :

sender(sender),

canceled(false) {

}


/**

* \brief Emptyvirtual destructor

*/

virtual ~Event() {}



/**

* \brief Gets thesource object for this event

*

* @return Theevent sender

*/

Object &getSender() {

return sender;

}


/**

* \brief Getswhether the event has been canceled

*

* @return true ifthe event is canceled

*/

bool getCanceled(){

return canceled;

}


/**

* \brief Sets thecanceled status for the event

*

* @param canceledWhether the even is canceled or not

*/

voidsetCanceled(bool canceled) { //事件发射出去之后,还可以取消吗?应该是在未调用事件处理函数之前吧?

this->canceled= canceled;

}

private:

Object &sender; 这里是记录了一下事件的发送者

bool canceled;

};

事件发送出来后,总得先有个地方存储呀,来看一个EventBus这个类。

#ifndef_SRC_EVENT_EVENT_BUS_HPP_

#define_SRC_EVENT_EVENT_BUS_HPP_

#include"Object.hpp"

#include"EventHandler.hpp"

#include "Event.hpp"

#include"HandlerRegistration.hpp"

#include <list>

#include <typeinfo>

#include<unordered_map>


/**

* \brief An Eventsystem that allows decoupling of code through synchronous events

*

*/

class EventBus :public Object { 所有的Event都派出于Object,这里的EventBus也派生于此。

public:

/**

* \brief Defaultempty constructor

*/

EventBus() { }



/**

* \brief Emptyvirtual destructor

*/

virtual ~EventBus(){ }



/**

* \brief Returnsthe EventBus singleton instance

*

* Creates a newinstance of the EventBus if hasn't already been created

*

* @return Thesingleton instance

*/

static EventBus*const GetInstance() { 这里使用静态函数,这就保证了系统中只有一个对象。

if (instance ==nullptr) { 使用该类的方法时直接使用就可以了。

instance = newEventBus();

}

return instance;

}



/**

* \brief Registersa new event handler to the EventBus with a source specifier

*

* The templateparameter is the specific type of event that is being added. Since aclass can

* potentiallyinherit multiple event handlers, the template specifier will removeany ambiguity

* as to whichhandler pointer is being referenced.

*

* @param handlerThe event handler class

* @param senderThe source sender object

* @return AnEventRegistration pointer which can be used to unregister the eventhandler

*/

template <classT>

staticHandlerRegistration* const AddHandler(EventHandler<T> &handler, Object & sender) {

EventBus* instance= GetInstance();


// Fetch the listof event pairs unique to this event type

Registrations*registrations = instance->handlers[typeid(T)];


// Create a newcollection instance for this type if it hasn't been created yet

if (registrations== nullptr) {

registrations =new Registrations();

instance->handlers[typeid(T)]= registrations;

}


// Create a newEventPair instance for this registration.

// This will groupthe handler, sender, and registration object into the same class

//这是将事件处理函数,发送者和注册对象封装在一个对象中。

EventRegistration*registration = new EventRegistration(static_cast<void*>(&handler),registrations,&sender);


这里应该是使用了链表的方式来存储,映射关系。

// Add theregistration object to the collection

registrations->push_back(registration);


returnregistration;

}



/**

* \brief Registersa new event handler to the EventBus with no source specified

*

* @param handlerThe event handler class

* @return AnEventRegistration pointer which can be used to unregister the eventhandler

*/

template <classT>

staticHandlerRegistration* const AddHandler(EventHandler<T> &handler) {

EventBus* instance= GetInstance();


// Fetch the listof event pairs unique to this event type

Registrations*registrations = instance->handlers[typeid(T)];


// Create a newcollection instance for this type if it hasn't been created yet

if (registrations== nullptr) {

registrations =new Registrations();

instance->handlers[typeid(T)]= registrations;

}


// Create a newEventPair instance for this registration.

// This will groupthe handler, sender, and registration object into the same class

EventRegistration*registration = new EventRegistration(static_cast<void*>(&handler),registrations, nullptr);


// Add theregistration object to the collection

registrations->push_back(registration);


returnregistration;

}



/**

* \brief Fires anevent

*

* @param e Theevent to fire

*/

//投递(发射)一个事件。这里比较关键,看一下是如何实现的。

static voidFireEvent(Event & e) {

EventBus* instance= GetInstance();


//发射一个事件时,根据事件从链表中获取注册对象(包括事件,事件处理函数,发送者)。

Registrations*registrations = instance->handlers[typeid(e)];


// If theregistrations list is null, then no handlers have been registered forthis event

if (registrations== nullptr) {

return;

}


// Iterate throughall the registered handlers and dispatch to each one if the sender

// matches thesource or if the sender is not specified

//遍历所有的注册对象,依次调用跟事件绑定在一起的事件处理函数。

//这里应该说是同步方式,因为一个事件处理函数不返回,下一个必须等待其执行完毕返回,才能执行。

for (auto &reg : *registrations) {

if((reg->getSender() == nullptr) || (reg->getSender() ==&e.getSender())) {


// This is wheresome magic happens. The void * handler is statically cast to an eventhandler

// of generictype Event and dispatched. The dispatch function will then do adynamic

// cast to thecorrect event type so the matching onEvent method can be called

//C++有个动态联编的特性这里使用类型转换,可以强制调用类型适应的函数。

static_cast<EventHandler<Event>*>(reg->getHandler())->dispatch(e);

}

}

}



private:

// Singleton classinstance

static EventBus*instance; 这里是静态的对象指针呀。



/**

* \briefRegistration class private to EventBus for registered event handlers

*/

这个是私有类,用于将事件、事件处理函数、发送者组织在一个类中。

classEventRegistration : public HandlerRegistration

{

public:

一个事件可能会有多个事件处理函数,所以这里使用列表保存。

typedefstd::list<EventRegistration*> Registrations;



/**

* \briefRepresents a registration object for a registered event handler

*

* This object isstored in a collection with other handlers for the event type.

*

* @param handlerThe event handler

* @paramregistrations The handler collection for this event type

* @param senderThe registered sender object

*/

EventRegistration(void* const handler, Registrations * const registrations, Object * constsender ) :

handler(handler), 事件处理函数

registrations(registrations),事件对象

sender(sender),事件发送者

registered(true)

{ }



/**

* \brief Emptyvirtual destructor

*/

virtual~EventRegistration() { }



/**

* \brief Gets theevent handler for this registration

*

* @return Theevent handler 返回事件处理函数指针

*/

void * constgetHandler() {

return handler;

}



/**

* \brief Gets thesender object for this registration

*

* @return Theregistered sender object

*/

Object* constgetSender() { 返回事件发送者(事件注册者)

return sender;

}



/**

* \brief Removesan event handler from the registration collection

*

* The eventhandler will no longer receive events for this event type

*/

virtual voidremoveHandler() { 移除事件处理函数

if (registered) {

registrations->remove(this);

registered =false;

}

}


private:

void * consthandler; 事件处理函数指针

Registrations*const registrations; 事件对象指针

Object* constsender; 发送者指针。


bool registered;

};


typedefstd::list<EventRegistration*> Registrations;

typedefstd::unordered_map<std::type_index,std::list<EventRegistration*>*> TypeMap;


TypeMap handlers;


};

#endif /*_SRC_EVENT_EVENT_BUS_HPP_ */

// Declare thestatic instance since this can't be done in the header file

EventBus*EventBus::instance = nullptr;


#ifndef_SRC_EVENT_HANDLER_REGISTRATION_HPP_

#define_SRC_EVENT_HANDLER_REGISTRATION_HPP_


#include"Object.hpp"


/**

* \brief Interfacethat that allows event handlers to be removed from the EventBus

*/

允许从事件总线上移除事件处理函数,纯虚函数,不实现,则不支持移除。

classHandlerRegistration : public Object {

public:

virtual~HandlerRegistration() { }


virtual voidremoveHandler() = 0;

};


#endif /*_SRC_EVENT_HANDLER_REGISTRATION_HPP_ */


再来看一下,事件处理函数

#ifndef_SRC_EVENT_EVENT_HANDLER_HPP_

#define_SRC_EVENT_EVENT_HANDLER_HPP_

#include"Object.hpp"

#include <typeinfo>

#include<type_traits>

// Forward declarethe Event class

class Event;


/**

* \brief Base classof all classes that listen for events

*

* For a class to bean event listener, it needs to inherit from EventHandler

* with the specificevent type as the template parameter. A class can inherit from

* multipleEventHandler base classes each using a different template parameter.

*/

template <classT>

class EventHandler {

public:


/**

* \brief Defaultconstructor that enforces the template type

*/

EventHandler() {

// An error hereindicates you're trying to implement EventHandler with a type that isnot derived from Event

static_assert(std::is_base_of<Event,T>::value, "EventHandler<T>: T must be a class derivedfrom Event");

}



/**

* \brief Emptyvirtual destructor

*/

virtual~EventHandler() { }



/**

* \brief Purevirtual method for implementing the body of the listener

*

* @param The eventinstance

*/

virtual voidonEvent(T &) = 0; 纯虚函数呀。必须被派出类实现!!



/**

* \briefDispatches a generic event to the appropriate listener method

*

* This method iscalled by the EventBus and dispatches to the correct method by

* dynamic castingthe event parameter to the template type for this handler.

*

* @param e Theevent to dispatch

*/ 如果调用这个分发事件的方法,就是调用内部函数onEvent()呀。

void dispatch(Event& e) {

onEvent(dynamic_cast<T&>(e));

}

};

#endif /*_SRC_EVENT_EVENT_HANDLER_HPP_ */



具体的事件类了,都继承自Event基类。

玩家聊天类,玩家聊天时发射此事件,主要是包括发送者,玩家,聊天的文本。

#ifndef_SRC_EVENT_PLAYER_CHAT_EVENT_HPP_

#define_SRC_EVENT_PLAYER_CHAT_EVENT_HPP_

#include "Event.hpp"

#include"Player.hpp"

#include <string>

classPlayerChatEvent : public Event

{

public:

PlayerChatEvent(Object& sender, Player & player, std::string const & msg) :

Event(sender),

player(player),

msg(msg) {

}


virtual~PlayerChatEvent() { }


Player &getPlayer() {

return player;

}


std::string const &getMessage() {

return msg;

}


private:

Player &player;

std::string const &msg;


};

#endif /*_SRC_EVENT_PLAYER_CHAT_EVENT_HPP_ */

下一个是玩家移动的事件,当玩家移动时,发射此事件,主要是包括了发送者,玩家,3维坐标。

#ifndef_SRC_EVENT_PLAYER_MOVE_EVENT_HPP_

#define_SRC_EVENT_PLAYER_MOVE_EVENT_HPP_


#include "Event.hpp"

#include"Player.hpp"

#include <string>

/**

* \brief Exampleevent class to showcase some of the features of the EventBus

*

* This is not partof the core functionality and can be modified or deleted as desired

*/

classPlayerMoveEvent : public Event

{

public:

PlayerMoveEvent(Object& sender, Player & player, int oldX, int oldY, int oldZ) :

Event(sender),

player(player),

oldX(oldX),

oldY(oldY),

oldZ(oldZ) {

}


virtual~PlayerMoveEvent() { }


Player &getPlayer() {

return player;

}


int getOldX() {

return oldX;

}


int getOldY() {

return oldY;

}


int getOldZ() {

return oldZ;

}


private:

Player &player;


int oldX;

int oldY;

int oldZ;

};


我们再来看一下玩家的定义,比较简单,就是名字+坐标。

#ifndef_SRC_PLAYER_HPP_

#define_SRC_PLAYER_HPP_


#include"Object.hpp"

//#include"PlayerMoveEvent.hpp"


#include <string>


classPlayerMoveEvent;


/**

* \brief Demo classto showcase some of the features of the EventBus

*

* This is not partof the core functionality and can be modified or deleted as desired

*/

class Player :public Object

{

public:

Player(std::stringname) :

name(name),

posX(0),

posY(0),

posZ(0)

{ }


virtual ~Player() {


}


const std::string &getName() {

return name;

}


voidsetPosition(int x, int y, int z) {

posX = x;

posY = y;

posZ = z;

}


int getX() {

return posX;

}


int getY() {

return posY;

}


int getZ() {

return posZ;

}


private:

std::string name;

int posX;

int posY;

int posZ;

};

#endif /*_SRC_PLAYER_HPP_ */


好了,基本上框架差不多,再来从main()入手,分析执行流程:

int main()

{

printf("* * *EventBus Demo Program * * * \n");


try

{

EventBusDemo demo;

demo.Demo1();

}

catch(std::runtime_error & e)

{

printf("Runtimeexception: %s\n", e.what());

}

}

来看一下EventBusDemo类吧:

/**

* \brief Demo classshowing off some functionality of the EventBus

*/

class EventBusDemo :public Object

{

public:

EventBusDemo() {

playerMoveReg =nullptr;

playerChatReg =nullptr;

}


virtual~EventBusDemo() { }



/**

* Demo Function 1

*

* Registers anevent listener on player1 and shows how events can be fired andcanceled

*/

void Demo1() {


// Two uniqueplayer objects

Playerplayer1("Player1"); 定义了2个玩家。只能了名字,没有给坐标,所以默认的xyz都为0.

Playerplayer2("Player2");


// Declare a localPlayerMoveEvent and use the event bus to fire it

// There arecurrently no listeners so this won't actually do anything

PlayerMoveEvente(*this, player1, 0, 0, 0); 向事件总线上发射一个玩家移动的事件。

EventBus::FireEvent(e); 因为没有绑定事件处理函数,所以不会执行什么。


// Create theplayer listener instance

PlayerListenerplayerListener; 玩家监听,用于监听来自玩家的事件。


// Register theplayer listener to handler PlayerMoveEvent events

// Passing player1as a second parameter means it will only listen for events from thatobject

// The returnvalue is a HandlerRegistration pointer that can be used to unregisterthe event handler

向总线注册,来自玩家player1的玩家移动事件PlayerMoveEvent将由playerListener来处理。

泥玛,这就是将事件与事件处理函数绑定呀。或者叫映射,搞得这么复杂!!!

playerMoveReg =EventBus::AddHandler<PlayerMoveEvent>(playerListener, player1);


// TheplayerListener gets registered again, but this time as player chatevent handler

// The lack of asecond parameter means that it will service ALL player chat events,

// regardless ofthe source

向总线注册,所有玩家的聊天事件PlayerChatEvent将由playerListener来处理。

playerChatReg =EventBus::AddHandler<PlayerChatEvent>(playerListener);



int x = 0;


// This loop willattempt to increase the X position of player one

// by 200 until itreaches 1000 or if the setPosition function fails.


// ThePlayer.setPosition() method fires a PlayerMoveEvent event internally

//

// ThePlayerListener class has an event handler that will cancel the

// PlayerMoveEventif the X position is greater than 500

while (x <=1000) {

printf("Changingplayer 1 X to %d\n", x);


// This methodwill fail once X > 500 because of the event handler we registered

if(setPlayerPostionWithEvent(player1, x, 0, 0) == true) {

x += 200;

} else {

printf("Settingplayer 1 position was canceled\n");

break;

}

}


x = 0;


// This loop doesthe same thing as the loop above, just with player2.

// Since we onlyregistered the PlayerListener to player1, the bounds

// checking willhave no effect for this loop

//

// This shows howan event handler will handle data from one source while ignoringothers

while (x <=1000) {

printf("Changingplayer 2 X to %d\n", x);

if(setPlayerPostionWithEvent(player2, x, 0, 0) == true) {

x += 200;

} else {

printf("Settingplayer 2 position was canceled\n");

break;

}

}



// Here two chatplayer chat events are created for each player and fired.

// Since the chatlistener was registered without a source object, it will service

// all chat eventsand print both messages

//

// The eventhandler will print out the player name with the message when theevent is fired

PlayerChatEventchat1(*this, player1, "Hello I am Player 1!");

EventBus::FireEvent(chat1);


PlayerChatEventchat2(*this, player2, "Hello I am Player 2!");

EventBus::FireEvent(chat2);



// TheHandlerRegistration object can be used to unregister the eventlistener

playerChatReg->removeHandler();



// If a chat eventis fired again, it will not be serviced since the handler has beenunregistered

PlayerChatEventchat3(*this, player2, "This chat message will not be serviced");

EventBus::FireEvent(chat3);



// Clean up

playerMoveReg->removeHandler();

deleteplayerMoveReg;

deleteplayerChatReg;

}


private:

HandlerRegistration*playerMoveReg;

HandlerRegistration*playerChatReg;



boolsetPlayerPostionWithEvent(Player & player, int x, int y, int z) {


int savedX =player.getX();

int savedY =player.getY();

int savedZ =player.getZ();


player.setPosition(x,y, z);


向总线发射玩家移动事件

PlayerMoveEvente(player, player, savedX, savedY, savedZ);

EventBus::FireEvent(e);其中在这一步中,是阻塞同步执行的,只有执行完后,才能返回。


if(e.getCanceled()) {

player.setPosition(savedX,savedY, savedZ);

return false;

}


return true;

}

};

还有一个关键的类!

/**

* \brief Simpleexample of an event handler class

*

* This snippetshows how to implement multiple EventHandlers in a single class

*/

class PlayerListener: public EventHandler<PlayerMoveEvent>, publicEventHandler<PlayerChatEvent>

{

public:

PlayerListener() {}


virtual~PlayerListener() { }



/**

* \brief Thisevent handler keeps the player inside a specific border area

*

* @param e ThePlayerMoveEvent event

*/

virtual voidonEvent(PlayerMoveEvent & e) override {


// Ignore theevent if it's already been canceled

if(e.getCanceled()) {

return;

}


Player & p =e.getPlayer();


// Cancel theevent if the new player position is outside of the border area

if(std::abs(p.getX()) > BORDER_SIZE || std::abs(p.getZ()) >BORDER_SIZE) {

e.setCanceled(true);

printf("Canceledsetting player %s position - outside of border\n",p.getName().c_str());

return;

}

}



/**

* This eventhandler prints out a debug message whenever a chat event is fired

*

* @param e ThePlayerChatEvent event

*/

virtual voidonEvent(PlayerChatEvent & e) override {


// Ignore theevent if it's already been canceled

if(e.getCanceled()) { 事件在未处理之前,如果已经被取消则忽略不处理它。

return;

}


printf("Theplayer '%s' said: %s\n", e.getPlayer().getName().c_str(),e.getMessage().c_str());

}


private:

static const intBORDER_SIZE = 500;

};


总结一下啦:

--Object

--------------------------Event

-----------------------------------PlayChatEvent

-----------------------------------PlayMoveEvent

---------------------------EventBus



Object派出生Event事件基类,再从Event派生出PlayChatEventPlayMoveEvent

Object派出生EventBus事件总线,是一个静态全局函数,一个进程中只有一个。EventBus使用链表组织数据,

EventRegistration将事件与事件处理函数绑定在一起,注册到EventBus中(就是添加到链表中)。

当调用EventBus静态函数的FireEvent()发射事件时,EventBus遍历链表,找到注册该事件的EventRegistration,然后再找到事件处理函数,

然后调用事件处理函数的dispatch()函数,从而间接调用了纯虚函数onEvent(),各派出类可自己实现。


EventHandler是事件处理基类,PlayerListener是派出类,重写了onEvent()函数。

对于简单的项目来说,应用这个架构来说有点小题大作,对于大项目来说,这个框架是同步阻塞单线程的,不适合在大项目中应用。

只是提供了一种思路。记得以前写VC6.0的程序时,就是映射ON_MESSAGE(WM_COMMAND_XX,OnWindowResize);之类的代码,

用于将事件跟事件处理函数绑定。估计思想就是这里说的差不多吧。


如果能将这个改造成多线程的,那就更加完美的啦。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值