【步兵 lua】事件模型和事件解耦
2017-03-26 13:03
393 查看
【步兵 lua】事件模型和事件解耦 By EOS.
因为大多数程序是单线程,如果想处理外部事件,都离不开消息循环,而事件模型和消息循环是天生一对,先了解下事件模式~
事件模型的特点
(1)可动态增加减少接收者(2)可动态调整接收优先级
(3)每个接收对象都有机会响应事件,每个事件可由多个对象同时处理
(4)对于处理不了的事件,接收对象可以不做处理
(5)对于已经处理的事件,接收对象可以选择停止派发,也可以选择继续派发
对应关系:
(1)addListener
(2)changeListenerPriority
(3)addEvent、addListener、broadcast
(4)recvFunc 随意处理接受到的事件
(5)recvFunc – return def.event.ret_break 中断此消息的广播
源码
--Event.lua Event = class("Event") function Event:init(name, args) self.name = name self.args = args end EventListener = class("EventListener") function EventListener:init(name, recvFunc, priority) self.name = name self.recv = recvFunc self.priority = priority or 0 end --EventDispatcher.lua EventDispatcher = class("EventDispatcher") local cls = EventDispatcher local EventListener = { name = "error", priority = 0, excute = function (event) end, } function cls:init() self._listeners = {} self._indexs = {} self._prioritys = {} self._events = {} self._maskEvents = {} end function cls:addEvent(event) self._events[event] = true end function cls:maskEvent(eventName) self._maskEvents[eventName] = true end function cls:cancelMaskEvent(event) self._maskEvents[eventName] = nil end function cls:cancelAllMaskEvent() self._maskEvents = {} end function cls:addListener(listener) local name = listener.name local priority = listener.priority or 0 local _listeners = self._listeners local _indexs = self._indexs local _prioritys = self._prioritys _listeners[name] = _listeners[name] or {} _listeners[name][priority] = _listeners[name][priority] or {} _indexs[name] = _indexs[name] or {} _indexs[name][priority] = _indexs[name][priority] or 0 _prioritys[name] = _prioritys[name] or {} local l_list = _listeners[name][priority] _indexs[name][priority] = _indexs[name][priority] + 1 local index = _indexs[name][priority] l_list[index] = listener if not table.find(_prioritys[name], priority) then table.insert(_prioritys[name], priority) table.sort(_prioritys[name], function (a, b) return a > b end) end return name.."_"..priority.."_"..index end function cls:removeListener(id) local tags = string.split(id, "_") local name = tags[1] local priority = tonumber(tags[2]) local index = tonumber(tags[3]) local _listeners = self._listeners if _listeners[name] and _listeners[name][priority] then _listeners[name][priority][index] = nil end end function cls:changeListenerPriority(id, newPriority) local tags = string.split(id, "_") local name = tags[1] local priority = tonumber(tags[2]) local index = tonumber(tags[3]) local _listeners = self._listeners if _listeners[name] and _listeners[name][priority] then local listener = _listeners[name][priority][index] _listeners[name][priority][index] = nil listener.priority = newPriority self:addListener(listener) end end function cls:clear() self._listeners = {} self._indexs = {} self._prioritys = {} end function cls:broadcast() print("broadcast event:") for event,_ in pairs(self._events) do if not self._maskEvents[event.name] then table.dump(event) local _listeners = self._listeners local _prioritys = self._prioritys local name = event.name local listeners = _listeners[name] if listeners then for i=1, #_prioritys[name] do local priority = _prioritys[name][i] for k,v in pairs(listeners[priority]) do local ret = v.recv(event.args) if ret == def.event.ret_break then return end end end end end end end
测试代码:
local dispatcher = EventDispatcher.new() local event = Event.new(def.event.TestOne, {tab = "this is event One"}) local listener = EventListener.new(def.event.TestOne, function(evt) print("recv: "..evt.tab) end, 1) local listener2 = EventListener.new(def.event.TestOne, function(evt) print("recv_2: "..evt.tab) -- return def.event.ret_break end, 2) local lsr_id = dispatcher:addListener(listener) local lsr_id_2 = dispatcher:addListener(listener2) dispatcher:addEvent(event) -- dispatcher:maskEvent(def.event.TestOne) -- dispatcher:cancelAllMaskEvent() -- dispatcher:removeListener(lsr_id_2) -- dispatcher:changeListenerPriority(lsr_id_2, 0) -- print(listener2.priority) dispatcher:broadcast() end
为什么说事件可以解耦
正因为是单线程所以程序是顺序执行的。而事件系统却可以使其分离,实现模块化。下面简单举个例子:
比如使用量个道具,人物升级了,刷新对应显示人物等级的地方。
方式一:
bag[useItem -> player[expAdd-> levelUp] -> ui_1[fresh] -> ...ui_n[fresh] -> end]
这样我使用一个道具的流程如果想结束。就必须背负着 人物属性相关模块
以及N个属性显示相关的模块,可能引发的问题就是,别人其中一个界面出了问题,
会导致我的模块报错,而我处理并没有任何问题,这就是耦合了。
方式二:
player[addListener(AddExp)] ui_1[addListener(LevelUp)] ... ui_n[addListener(LevelUp)] bag[useItem -> addEvent(AddExp) -> end] player[recv(AddExp) -> addEvent(LevelUp)] ui_1[recv(LevelUp)] ... ui_n[recv(LevelUp)]
效果是不是一目了然,各个模块都清晰的分离开了。这就是解耦~
因为我们用事件模式,“延迟调用”的方式(先添加,不处理),才得以实现。
总结
实现了一个独立的事件模型,但还有值得优化的地方。我认为事件模型能解偶,主要在于延迟调用,如果即时调用的那发送事件是没意义的。
无非就是a->b->end变为了 a->c->b->end而已。
当然,事件过多的应用,可能会是逻辑过于分散,不容易调试。
不过只要规划好事件名就可以了,因为事件名就是这些逻辑的主心骨~ =、=
See Again~
之前
真爱无价,欢迎打赏~
相关文章推荐
- iphonewax下用lua代码addTarget_action_forControlEvents给UIButton添加事件程序crash解决办法
- Netbios编程(一):服务器端(事件模型)
- Cocos2d-x 3.0 Lua编程 之 响应物理引擎的Contact事件回调不执行的问题
- .net事件模型
- GUI事件模型ActionEvent举例,TextField事件监听举例
- JS 事件处理模型
- 深入Android开发之--Android事件模型
- 事件模型学习
- 事件模型兼容代码
- 4、事件模型,冒泡,捕捉
- Delphi编写事件模型客户端(1)
- Winsock的事件I/O异步模型(开发网络通信程序入门的继续)
- javascript中0级DOM和2级DOM事件模型浅析
- 事件驱动的编程模型(学习)
- 《jQuery从入门到精通》第十七节 jQuery 事件模型
- javascript事件模型框架-eventutil.js
- 类成员函数作为回调函数/事件模型
- 回调函数与Delphi的事件模型
- nginx 工作原理,进程模型,事件处理,配置系统和模块化体系
- 面试题-冒泡与捕获、事件委托、ie事件和dom模型事件、鼠标事件