基于lua的观察者模式应用:事件分发系统

这篇博客探讨了事件系统在处理回调函数时的关键点,包括事件订阅的优先级、派发过程中的事件处理以及如何在派发过程中安全地取消订阅。文章详细介绍了事件注册、排序、取消以及派发的实现,特别强调了在事件派发过程中不能进行优先级排序以防止回调重复触发的问题。同时,还展示了如何标记并清理在派发过程中需要删除的监听器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

local b = class()
function B:on_money_change(money)
	print(money,"B recive event")
end
EventSystem:on(Event.MoneyChanged,B.on_money_change,{target = B})
--定义回调函数和注册事件
local c = class()
function C:on_money_change(money)
	print(money,"C recive event")
end
EventSystem:on(Event.MoneyChanged,C.on_money_change,{target = C})

EventSystem:emit(Event.MoneyChanged,10)

关键点:

  1. 在事件派发的过程中订阅该事件,订阅还有优先级,需要小心处理排序问题
  2. 在事件派发的过程中取消订阅该事件,需要采用标记移除,不能直接移除
  3. 在事件派发的过程中又派发了该事件,如何确定事件派发完成
--注册接口实现
function EventSystem:on( event, func, params )
    --- ...
    local event_listener = self._listeners[event]
    params = params or {}
    local priority = params.priority or 0 --事件优先级设定
    local target = params.target
    --- ...
    local cb = {target = target, func = func, id = id, priority = priority}
    table.insert(event_listener.list, cb)
    id = id + 1
    if priority > 0 then
        event_listener.need_sort = true
        self:sort(event_listener)
    end
end
--排序事件
function EventSystem:sort( listener )
    if listener.need_sort == true and listener.emit_count == 0 then
    -- listener.emit_count == 0 当前事件没有处于派发状态
    --[[事件处于派发状态时不能进行优先级排序原因是可能会造成回调的重复触发。
       比如当前事件有4个回调 a, b, c, d,派发事件是顺序执行回调,当执行到第3个回调c时,如果在c回调中                  又注册了一个优先级最高的回调e,立刻排序的话,e插入到第一位,c会被挤到第4位,顺序执行到第4个回调时,导致c又被调用一次
    ]]
        table.sort(listener.list, function ( a, b )
            if a.priority == b.priority then
                return a.id < b.id
            else
                return a.priority > b.priority
            end
        end)
        listener.need_sort = false;
    end
end
--监听取消函数
function EventSystem:off( event, func, params )
    --- ...
    local event_listener = self._listeners[event]
    params = params or {}
    for i,cb in ipairs(event_listener.list) do
        if cb.func == func and cb.target == params.target then
            if event_listener.emit_count > 0 then
                -- 派发过程中只进行标记删除
                cb.need_remove = true
                event_listener.need_clean = true
            else
                table.remove(event_listener.list, i)
            end
            break;
        end
    end
end
--事件派发接口
function EventSystem:emit( event, ... )
    --- ...
    local event_listener = self._listeners[event]
    local interrupt = false
    local length = #event_listener.list
    -- 这里不能使用ipairs,确保不会触发在派发过程中注册的事件
    -- 只取当前已经注册的事件数量,如果在派发过程中再注册(调用了table.insert),本次派发也不会调用
    for i = 1, length do
        if interrupt == true then
            break
        end
        local cb = event_listener.list[i]
        if cb.func and cb.need_remove ~= true then
            event_listener.emit_count = event_listener.emit_count + 1
            if cb.target then
                interrupt = cb.func(cb.target, ...)
            else
                interrupt = cb.func(...)
            end
            event_listener.emit_count = event_listener.emit_count - 1
        end
    end
    self:sort(event_listener);
    self:clean(event_listener);
    return interrupt
end
### 不使用闭包实现观察者模式的方法 在 Lua 中,可以通过多种方式实现观察者模式而不依赖于闭包。以下是基于引用内容和其他专业知识的一种具体实现方案。 #### 使用表和元方法实现观察者模式 通过利用 Lua 的表作为容器以及元方法的功能,可以构建一个简单的观察者模式框架。此方法的核心在于维护一组订阅者列表,并提供统一的通知机制。 以下是一个具体的实现: ```lua -- 定义 Subject 类(被观察的对象) local Subject = {} Subject.__index = Subject function Subject:new() local instance = setmetatable({}, Subject) instance.observers = {} -- 订阅者列表 return instance end function Subject:register(observer) table.insert(self.observers, observer) -- 注册新的观察者 end function Subject:deregister(observer) for i, obs in ipairs(self.observers) do if obs == observer then table.remove(self.observers, i) -- 移除指定的观察者 break end end end function Subject:notify(eventData) for _, observer in ipairs(self.observers) do if type(observer.update) == "function" then observer:update(eventData) -- 调用每个观察者的 update 方法 end end end -- 定义 Observer 类(观察者) local Observer = {} Observer.__index = Observer function Observer:new(updateFunc) local instance = setmetatable({}, Observer) instance.update = updateFunc -- 更新逻辑由外部传入 return instance end -- 测试代码 local subject = Subject:new() -- 创建两个观察者实例 local observer1 = Observer:new(function(data) print("Observer 1 received:", data) end) local observer2 = Observer:new(function(data) print("Observer 2 received:", data) end) -- 注册观察者到主体 subject:register(observer1) subject:register(observer2) -- 发送通知给所有观察者 subject:notify("Event Data") -- 取消注册其中一个观察者 subject:deregister(observer1) -- 再次发送通知 subject:notify("Another Event") ``` 以上代码展示了如何通过显式的 `register` 和 `deregister` 函数管理观察者列表[^3]。每当主题状态发生变化时,会调用其 `notify` 方法向所有已注册的观察者广播消息。 #### 关键点解析 - **表结构**:使用 Lua 表来保存所有的观察者对象及其回调函数。 - **元方法支持**:借助 `setmetatable` 设置自定义行为,使类具有继承特性[^4]。 - **灵活性高**:无需依赖闭包即可完成复杂的事件分发流程。 这种设计方案不仅易于扩展还保持了良好的可读性和性能表现[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值