分析一下基于事件的程序架构,程序中所有的对象之间的通信都是通过事件总线,
当一个对象需要执行一个动作时,他向事件总线发射一个事件,然后事件总线根据之前注册的
事件与事件处理函数,找到事件对应的事件处理函数并调用它。
这个是比较简单的同步模式,没有应用多线程,是老外写的一个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 ® : *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派生出PlayChatEvent和PlayMoveEvent
从Object派出生EventBus事件总线,是一个静态全局函数,一个进程中只有一个。EventBus使用链表组织数据,
EventRegistration将事件与事件处理函数绑定在一起,注册到EventBus中(就是添加到链表中)。
当调用EventBus静态函数的FireEvent()发射事件时,EventBus遍历链表,找到注册该事件的EventRegistration,然后再找到事件处理函数,
然后调用事件处理函数的dispatch()函数,从而间接调用了纯虚函数onEvent(),各派出类可自己实现。
EventHandler是事件处理基类,PlayerListener是派出类,重写了onEvent()函数。
对于简单的项目来说,应用这个架构来说有点小题大作,对于大项目来说,这个框架是同步阻塞单线程的,不适合在大项目中应用。
只是提供了一种思路。记得以前写VC6.0的程序时,就是映射ON_MESSAGE(WM_COMMAND_XX,OnWindowResize);之类的代码,
用于将事件跟事件处理函数绑定。估计思想就是这里说的差不多吧。
如果能将这个改造成多线程的,那就更加完美的啦。