Lua 之 Class 强化 - 持续优化迭代中

  • 为什么要自己写 Class
  • Lua Class 能干什么
  • 特性示例
    • 类(继承,方法重写)
    • 属性 Getter
    • 属性 Setter
    • 拦截 getter/setter
    • 异步 async/await + Promise【详细可参考 javascript 中的 Promise 用法】
    • 方法锁定
    • 属性变化监听
    • 模块通信(可以参考 MVC 的通知机制,建议只在逻辑类中使用该功能,因为该类会作为全局单例使用,而且不提供接口获取该单例,正常使用为一个独立的功能模块对外的接口,保证模块间解耦)
    • 信号(类似于事件派发器,但不需要定义事件类型,类似于 C# 的 `Delegate`)
  • 原创文章,转载请注明出处:Lua 之 Class 强化
  • lua 库下载

为什么要自己写 Class

  Lua 作为一门脚本语言,提供了基本的逻辑处理能力(编写简单的算法处理数据),但在面对复杂的应用时(整个游戏都用 Lua 编写)还是需要能对代码有较友好的管理和编写方式来支持,以增强代码稳定性和减少后期维护成本。
  面向对象是大多数语言(C++, C#, Java, TypeScript 等)的标配,以后相应提供的各种语言糖让我们编写代码时得心应手。那 Lua 能不能也拥有其它语言相应的特性呢?
  在 github 上找过,也有过很多 lua class 可以使用,但基本就只实现了类的继承一些简单的面向对象的功能,而且源码可读性真不咋样,想扩展一下也挺费力的,在学习多个开源 class 后觉得靠自己能力也能写出来,所在就有了这篇文章。

Lua Class 能干什么

  • 类(继承,方法重写)
  • 属性 Getter/Setter
  • 拦截 getter/setter
  • 异步 async/await + Promise
  • 方法锁定
  • 属性变化监听
  • 模块通信
  • 信号

特性示例

  • 类(继承,方法重写)

    • 实现最基础的类定义,子类继承与方法重写,默认所有自定义类的基类都为 Object
    • 支持运算符重载
      • ctor fun(...:any):void @构造函数,所有子类(Object 的直接基类除外)都需要调用基类 ctor
      • call fun(...:any):any @对象执行函数
      • add fun(target:Object):Object @加号重载
      • sub fun(target:Object):Object @减号重载
      • equalTo fun(target:Object):boolean @等于重载
      • lessThan fun(target:Object):boolean @小于重载
      • lessEqual fun(target:Object):boolean @小于等于重载
      • toString fun():string @转换成字符串
    • 常用工具方法
      • xx.findType(className:string):Type 获取指定类名的类型
      • xx.isType(target:any):boolean 判断指定对象是否是类型
      • xx.isSubType(type:Type, parentType:Type):boolean 判断类型是否是指定类型子类
      • xx.instanceOf(target:any, type:Type):boolean 判断指定对象是否是指定类型实例
------------------------------文件 Animal.lua------------------------------
---动物基类
---@class Animal:Object @by wx771720@outlook.com 2021-02-23 18:14:22
---@field name string @名字
local Animal = xx.Class("Animal")
---构造函数
function Animal:ctor(name)self.name = name
end
function Animal:sayHello()error("must override by subclass")
end
return Animal
------------------------------文件 Cat.lua------------------------------
---@type Animal
local Animal = require "Animal"
---猫
---@class Cat:Animal @by wx771720@outlook.com 2021-02-23 18:16:37
local Cat = xx.Class("Cat", Animal)
---构造函数
function Cat:ctor(name)Animal.ctor(self, name)
end
function Cat:sayHello()print(self.name .. "[Cat] say miao")
end
return Cat
------------------------------文件 Dog.lua------------------------------
---@type Animal
local Animal = require "Animal"
---狗
---@class Dog:Animal @by wx771720@outlook.com 2021-02-23 18:18:05
local Dog = xx.Class("Dog", Animal)
---构造函数
function Dog:ctor(name)Animal.ctor(self, name)
end
function Dog:sayHello()print(self.name .. "[Dog] say wang")
end
return Dog
------------------------------文件 Main.lua------------------------------
require "core"
---@type Cat
local Cat = require "Cat"
---@type Dog
local Dog = require "Dog"local cat = Cat("hello kitty")
cat:sayHello()local dog = Dog("snoopy")
dog:sayHello()
------------------------------执行 lua Main.lua 输出------------------------------
xx[lua] version: 0.0.1, Email : wx771720@outlook.com
hello kitty[Cat] say miao
snoopy[Dog] say wang
  • 属性 Getter

------------------------------文件 Animal.lua------------------------------
---动物基类
---@class Animal:Object @by wx771720@outlook.com 2021-02-23 18:14:22
---@field name string @[ReadOnly]名字 【修改】
---
---@field _name string @名字 【新增】
local Animal = xx.Class("Animal")
---构造函数
function Animal:ctor(name)self._name = name --【修改】
end
function Animal:nameGetter() -- 【新增】return self._name
end
function Animal:sayHello()error("must override by subclass")
end
return Animal
------------------------------文件 Main.lua------------------------------
require "core"
---@type Cat
local Cat = require "Cat"
---@type Dog
local Dog = require "Dog"local cat = Cat("hello kitty")
cat.name = "tom" -- 【新增】
cat:sayHello()local dog = Dog("snoopy")
dog.name = "pluto" -- 【新增】
dog:sayHello()
------------------------------执行 lua Main.lua 输出------------------------------
xx[lua] version: 0.0.1, Email : wx771720@outlook.com
hello kitty[Cat] say miao
snoopy[Dog] say wang
  • 属性 Setter

------------------------------文件 Animal.lua------------------------------
---动物基类
---@class Animal:Object @by wx771720@outlook.com 2021-02-23 18:14:22
---@field name string @[ReadOnly]名字
---
---@field _name string @名字
local Animal = xx.Class("Animal")
---构造函数
function Animal:ctor(name)self._name = name
end
function Animal:nameGetter()return self._name
end
function Animal:nameSetter(value) -- 【新增】print("name changed [" .. self._name .. "]=>[" .. value .. "]")self._name = value
end
function Animal:sayHello()error("must override by subclass")
end
return Animal
------------------------------执行 lua Main.lua 输出------------------------------
xx[lua] version: 0.0.1, Email : wx771720@outlook.com
name changed [hello kitty]=>[tom]
tom[Cat] say miao
name changed [snoopy]=>[pluto]
pluto[Dog] say wang
  • 拦截 getter/setter

------------------------------文件 Animal.lua------------------------------
---动物基类
---@class Animal:Object @by wx771720@outlook.com 2021-02-23 18:14:22
---@field name string @[ReadOnly]名字
---
---@field _name string @名字
local Animal = xx.Class("Animal")
---构造函数
function Animal:ctor(name)self._name = name
end
function Animal:getter(key) -- 【新增】print("get key : " .. key)return xx.rawGet(self, key) -- 原始方法获取值
end
function Animal:setter(key, value) -- 【新增】print("set key : " .. key .. " => " .. value)xx.rawSet(self, key, value) -- 原始方法设置值
end
function Animal:nameGetter()return self._name
end
function Animal:nameSetter(value)print("name changed [" .. self._name .. "]=>[" .. value .. "]")self._name = value
end
function Animal:sayHello()error("must override by subclass")
end
return Animal
------------------------------文件 Main.lua------------------------------
require "core"---@type Cat
local Cat = require "Cat"
---@type Dog
local Dog = require "Dog"local cat = Cat("hello kitty")
cat.name = "tom" -- 不生效
-- cat:sayHello() -- 【删除】
print(cat.name)local dog = Dog("snoopy")
dog.name = "pluto" -- 不生效
-- dog:sayHello() -- 【删除】
print(dog.name)
------------------------------执行 lua Main.lua 输出------------------------------
xx[lua] version: 0.0.1, Email : wx771720@outlook.com
set key : _uid => xx_lua_5
set key : _name => hello kitty
set key : name => tom
name changed [hello kitty]=>[tom]
get key : name
tom
set key : _uid => xx_lua_6
set key : _name => snoopy
set key : name => pluto
name changed [snoopy]=>[pluto]
get key : name
pluto
  • 异步 async/await + Promise【详细可参考 javascript 中的 Promise 用法】

    • 在类方法定义前添加标记Main.__plug_async = true表示该方法以协程中执行,并返回 Promise 对象
    • 只有在 __plug_async 标识过的方法内才允许使用 xx.await 来暂停当前方法
    • 只要是返回 Promise 对象的方法都可以作为 xx.await 的参数来等待该方法结束
------------------------------新建文件 Main.lua------------------------------
require "core"---程序入口类
---@class Main:Object @by wx771720@outlook.com 2021-02-23 19:22:24
local Main = xx.Class("Main")
---构造函数
function Main:ctor()
endMain.__plug_async = true
---登录
---@return Promise
function Main:login()print("login start")-- 等待配置更新完成xx.await {self:updateConfig()}print("login config updated")-- 等待资源下载完成xx.await {self:downloadAssets()}print("login assets downloaded")-- 等待登录请求返回的数据local response = xx.await {self:loginRequest()}print("login login response")
end---更新配置(异步 - 假装使用定时器来模拟)
---@return Promise
function Main:updateConfig()xx.await {self:sleep(1)}end
Main.__plug_async = true
---下载资源(异步 - 假装使用定时器来模拟)
---@return Promise
function Main:downloadAssets()xx.await {self:sleep(1)}
endMain.__plug_async = true
---发送登录请求(异步 - 假装使用定时器来模拟)
---@return Promise @返回登录返回的数据
function Main:loginRequest()xx.await {self:sleep(1)}return {}
end---将回调方式的异步(定时器)封装成可等待的形式
---@return Promise @在调用 resolve 时可以传入数据,通过 xx.await 返回
function Main:sleep(time)---@type Promiselocal promise = xx.Promise()-- 延迟指定时间调用 promise:resolve(),这里面的 delayCall 只是假设的项目中一个定时器接口(Lua 不提供定时器功能)-- delayCall(time, function() promise:resolve() end)return promise
end---构造实例
Main():login()return Main
  • 方法锁定

    • 在类方法定义前添加标记Main.__plug_lock = true表示在该方法执行过程中忽略再次调用
    • 如果该类方法返回 Promise 对象,则以 Promise 对象的 fulfilled 或者 rejected 状态表示该方法结束
    • 如果该类方法返回非 Promise 对象,则以方法最后一行代码执行结束表示该方法结束
------------------------------文件 Main.lua------------------------------
Main.__plug_async = true
Main.__plug_lock = true
---界面登录按钮点击监听 【新增】
---假如双击登录按钮(即连续调用该方法2次),因为该方法为异步(不可能在双击过程中执行结束),所以直接忽略第二次回调
---@return Promise
function Main:onClickHandler()xx.await {self:login()}
end
  • 属性变化监听

    • 添加标记 Main.__plug_watcher = xxx 表示需要监听属性变化
    • xxx 可直接为属性名字符串:表示该属性变化后派发默认事件 xx.e_property_changed
    • xxx 指定为对象时,第一个参数必须为属性名
      • 后面添加类方法表示属性改变时回调该类方法,参数:属性名,新值,旧值
      • 后面添加字符串表示自定义事件名,如果不添加自定义事件名,会派发默认事件 xx.e_property_changed
      • 仅在该类为 xx.EventDispatcher 子类时才会派发事件,但类方法回调方式支持任意类,事件回调参数为 Event 对象,其中 args 数据分别为:属性名,新值,旧值
------------------------------文件 Data.lua---------------------------------数据
---@class Data:EventDispatcher @by wx771720@outlook.com 2021-02-23 20:00:51
---@field avatar string @头像
---@field level number @等级
---@field coin number @金币
local Data = xx.Class("Data", xx.EventDispatcher)
---构造函数
function Data:ctor()xx.EventDispatcher.ctor(self)self:addEventListener("e_level_changed", self.onLevelChanged, self)self:addEventListener(xx.e_property_changed, self.onCoinChanged, self)
end---@param key string
---@param newValue string
---@param oldValue string
function Data:onAvatarChanged(key, newValue, oldValue)print("callback : " .. key .. " changed " .. tostring(oldValue) .. " => " .. tostring(newValue))
end
---@param evt Event
function Data:onLevelChanged(evt)--evt.args : key, newValue, oldValueprint("custom event : " .. evt.args[1]  .. " changed " .. tostring(evt.args[3]) .. " => " .. tostring(evt.args[2]))
end
---@param evt Event
function Data:onCoinChanged(evt)print("default event : " .. evt.args[1]  .. " changed " .. tostring(evt.args[2]) .. " => " .. tostring(evt.args[3]))
end---Callback 方式支持任意类方法
Data.__plug_watcher = {"avatar", Data.onAvatarChanged}
---自定义事件仅支持 EventDisptacher 子类
Data.__plug_watcher = {"level", "e_level_changed"}
---默认事件仅支持 EventDisptacher 子类
Data.__plug_watcher = "coin"return Data
------------------------------文件 Main.lua------------------------------
require "core"---@type Data
local Data = require "Data"local data = Data()
data.avatar = "001.jpg"
data.level = 1
data.coin = 0
print()
data.avatar = "002.jpg"
data.level = 999
data.coin = 99999999
------------------------------执行 lua Main.lua 输出------------------------------
xx[lua] version: 0.0.1, Email : wx771720@outlook.com
callback : avatar changed nil => 001.jpg
default event : avatar changed 001.jpg => nil
custom event : level changed nil => 1
default event : coin changed 0 => nilcallback : avatar changed 001.jpg => 002.jpg
default event : avatar changed 002.jpg => 001.jpg
custom event : level changed 1 => 999
default event : coin changed 99999999 => 0
  • 模块通信(可以参考 MVC 的通知机制,建议只在逻辑类中使用该功能,因为该类会作为全局单例使用,而且不提供接口获取该单例,正常使用为一个独立的功能模块对外的接口,保证模块间解耦)

    • 在类方法前添加标记 Main.__plug_on_notice 表示该方法为对外接口
    • 如果直接指定为字符串:表示该方法接口对外的通知名
    • 如果指定为对象(参数无序)
      • 字符串:表示该方法接口对外的通知名
      • 数值:表示有多个模块的接口监听同一个通知名时的调用优先级,数值越大越优先调用(常用于广播通知)
      • 布尔:表示该方法接口是否在触发(执行)一次后自动移除(即只触发一次)
    • 类方法第一个参数必须为 result:NoticeResult 对象,后续参数为 xx.notify(...) 参数
      • 所有返回数据只能通过 result.data = xxx 赋值返回
      • 在处理广播通知时,高优先级的通知监听可以通过 result:stop() 阻止后续低优先级的通知监听
------------------------------文件 LogModule.lua------------------------------
---日志模块(只能使用通知名通过 xx.notify() 调用该类的接口方法)
---@class LogModule:Object @by wx771720@outlook.com 2021-02-23 20:28:46
local LogModule = xx.Class("LogModule")
---构造函数
function LogModule:ctor()
endLogModule.__plug_on_notice = "ni_debug"
---@param result NoticeResult
function LogModule:onDebug(result, ...)print("log debug : ", ...)
endLogModule.__plug_on_notice = {"ni_init", 0, true}
---@param result NoticeResult
function LogModule:on(result, ...)print("log init : ", ...)
endreturn LogModule
------------------------------文件 Main.lua------------------------------
require "core"---@type LogModule
local LogModule = require "LogModule"xx.notify("ni_debug", 1, "02", nil, false)
xx.notify("ni_debug", 11, "022", nil, true)xx.notify("ni_init", 1, "02", nil, false)
xx.notify("ni_init", 11, "022", nil, true)
------------------------------执行 lua Main.lua 输出------------------------------
log debug :     1       02      nil     false
log debug :     11      022     nil     true
log init :      1       02      nil     false
  • 信号(类似于事件派发器,但不需要定义事件类型,类似于 C# 的 Delegate

    • 添加标记 Main.__plug_signal 表示定义一个信号,后直接为信号属性名
    • 信号对象可以直接调用(实现了元数据中的 call 方法),并且可以传入数据,在监听回调中可通过参数 evt:Eventevt.args 中访问
------------------------------文件 View.lua------------------------------
---视图逻辑类
---@class View:Object @by wx771720@outlook.com 2021-02-23 20:50:41
---@field onLogin Signal @登录按钮点击信号
local View = xx.Class("View")
---构造函数
function View:ctor()-- 添加界面按钮点击回调(非真实代码)-- self.ui.loginBtn:onClick = xx.Handler(self.onLoginClicked, self)
endView.__plug_signal = "onLogin"function View:onLoginClicked()self.onLogin()
endreturn View
------------------------------文件 Main.lua------------------------------
require "core"---@type View
local View = require "View"local view = View()
view.onLogin:addListener(function(evt)print("on login trigger", evt.target == view)
end)
-- 假装点击了界面上的登录按钮
view:onLoginClicked()
------------------------------执行 lua Main.lua 输出------------------------------
xx[lua] version: 0.0.1, Email : wx771720@outlook.com
on login trigger        true

Lua 之 Class 强化 - 持续优化迭代中相关推荐

  1. matlab与水库调度,蛙跳算法优化水库调度,全局迭代中最优解未更新

    本人尝试利用蛙跳算法求解水库单目标调度,目标函数是极大值函数,在运算中适应值最大的解在全局迭代中一直未更新,调试算法 发现子群内更新时按适用度降序排列第一个最优解也未更新,不知道问题出在哪里,求大神指 ...

  2. 阿里开发者们的第13个感悟:工程师需要在循环迭代中成长

    2015年12月20日,云栖社区上线.2018年12月20日,云栖社区3岁. 阿里巴巴常说"晴天修屋顶". 在我们看来,寒冬中,最值得投资的是学习,是增厚的知识储备. 所以社区特别 ...

  3. 微博计数:从关系服务到访问计数, Redis 持续优化支撑万亿级访问(含 PPT)

    关注"数据和云",精彩不容错过 编者按:本文由刘东辉向高可用架构供稿,基于在 Redis 用户交流会上的演讲内容整理.这是一篇历史文章,但是仍然可以帮助我们理解 Redis 的应用 ...

  4. 微信回应朋友圈广告无法一键关闭:将持续优化产品体验

    12月18日消息,12月16日,上海市消保委发布<APP广告消费者权益保护评价报告(2020)>,报告中点名微信等平台存在广告不能一键关闭的问题.对此,微信方面向媒体回应称,微信十分重视各 ...

  5. 手机银行APP评测系列:天津银行持续优化手机银行用户体验,但仍需加强细节提升

    易观分析:作为银行金融服务线上场景渗透的有效抓手,当前手机银行APP已经成为其触达用户的重要渠道.随着银行发力场景服务平台成为发展趋势,5G技术问世对金融服务场景端提出新要求,用户体验反馈成为银行线上 ...

  6. 订单贡献率10%,京东个性化推荐系统持续优化的奥秘

    京东基于大数据和个性化推荐算法,实现了向不同用户展示不同的内容的效果,在PC端和移动端都已经为京东贡献了10%的订单.为了探索京东全品类平台"千人千面"背后的算法奥妙,CSDN记者 ...

  7. 什么是强化学习,强化学习在控制系统中的应用以及matlab强化学习工具箱的介绍

    一.Reinforcement Learning Toolbox介绍 强化学习工具箱使用强化学习算法(包括DQN,A2C和DDPG)为训练策略(policy)提供函数和模块.您可以使用这些策略为复杂的 ...

  8. 为什么在优化算法中使用指数加权平均

    本文知识点: 什么是指数加权平均? 为什么在优化算法中使用指数加权平均? β 如何选择? 1. 什么是指数加权平均 指数加权平均(exponentially weighted averges),也叫指 ...

  9. 数据分析从零到精通第四课 在产品需求、开发、运营和迭代中进行数据分析

    08 数据平台:本机部署大数据平台工具及使用 你好,我是取经儿.上一课时我带你学习了快速搭建 BI 平台来构建报表.本课时我将手把手教你在本地电脑安装离线大数据处理工具 Hive.Hive 也是数据分 ...

最新文章

  1. Linux的load导入语句,LOAD DATA INFILE语句导入数据进入MySQL的一些注意事项
  2. 【CV】使用直方图处理进行颜色校正
  3. linux查看睡眠进程,关于 Linux 进程的睡眠和唤醒 ,来看这篇就够了~
  4. [渝粤教育] 广东-国家-开放大学21秋期末考试中国近现代史纲要(A)10881k1
  5. 使用ESP定律_手工脱壳
  6. java web购物车_java web开发——购物车功能实现
  7. 笨笨工作室告别十月模拟赛
  8. 一篇 vSAN 入门,送给大家
  9. pandas—总结(2) 数据读写 (更新中)
  10. ylbtech-LanguageSamples-Indexers_2(索引器)
  11. 信息学奥赛 java C 选_信息学奥赛(NOIP)入门指南(新家长必读)
  12. 天地图和谷歌地图静态图像素坐标和经纬度坐标互转
  13. opencv3中的图像混合操作
  14. 一篇文让你了解JAVA IO(超详细 基础篇)
  15. 计算机一级wps office考试题库,2017计算机一级WPSOffice复习题(含答案)
  16. Java/计算银行利率
  17. VC 使用IE、火狐或默认浏览器打开网页ShellExecute
  18. Python_文本分析入门_SnowNLP(1)
  19. 计算机专业前沿算法,CNCC2018 | 研究经典计算机算法已经过时了吗?
  20. android开机logo和动画修改

热门文章

  1. FreeNAS虚拟机系统安装
  2. [ZT]SUN非常有用之 Unix/Linux 单行脚本
  3. 【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务内容接口卡片接入指南
  4. 【PHP】PHP基本语法、PHP的数据类型、PHP数据的输出
  5. 将 bytes 转换成 str
  6. 淮安计算机教师考编试题及答案,2020年江苏省淮安市市直机关幼儿园幼儿教师招聘/编制考试历年真题试卷及参考答案...
  7. 看完这篇 Linux 权限后,通透了
  8. 数据结构(c)的学习总结
  9. python只能用一行代码_Python 一行代码能实现丧心病狂的功能
  10. 《数据仓库与数据挖掘教程》ch02数据仓库原理 章节整理