您的位置:首页 > 编程语言 > Lua

【步兵 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~

之前

真爱无价,欢迎打赏~

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息