Lua 之 Class 强化 - 持续优化迭代中
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
的直接基类除外)都需要调用基类 ctorcall 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:Event
的evt.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 强化 - 持续优化迭代中相关推荐
- matlab与水库调度,蛙跳算法优化水库调度,全局迭代中最优解未更新
本人尝试利用蛙跳算法求解水库单目标调度,目标函数是极大值函数,在运算中适应值最大的解在全局迭代中一直未更新,调试算法 发现子群内更新时按适用度降序排列第一个最优解也未更新,不知道问题出在哪里,求大神指 ...
- 阿里开发者们的第13个感悟:工程师需要在循环迭代中成长
2015年12月20日,云栖社区上线.2018年12月20日,云栖社区3岁. 阿里巴巴常说"晴天修屋顶". 在我们看来,寒冬中,最值得投资的是学习,是增厚的知识储备. 所以社区特别 ...
- 微博计数:从关系服务到访问计数, Redis 持续优化支撑万亿级访问(含 PPT)
关注"数据和云",精彩不容错过 编者按:本文由刘东辉向高可用架构供稿,基于在 Redis 用户交流会上的演讲内容整理.这是一篇历史文章,但是仍然可以帮助我们理解 Redis 的应用 ...
- 微信回应朋友圈广告无法一键关闭:将持续优化产品体验
12月18日消息,12月16日,上海市消保委发布<APP广告消费者权益保护评价报告(2020)>,报告中点名微信等平台存在广告不能一键关闭的问题.对此,微信方面向媒体回应称,微信十分重视各 ...
- 手机银行APP评测系列:天津银行持续优化手机银行用户体验,但仍需加强细节提升
易观分析:作为银行金融服务线上场景渗透的有效抓手,当前手机银行APP已经成为其触达用户的重要渠道.随着银行发力场景服务平台成为发展趋势,5G技术问世对金融服务场景端提出新要求,用户体验反馈成为银行线上 ...
- 订单贡献率10%,京东个性化推荐系统持续优化的奥秘
京东基于大数据和个性化推荐算法,实现了向不同用户展示不同的内容的效果,在PC端和移动端都已经为京东贡献了10%的订单.为了探索京东全品类平台"千人千面"背后的算法奥妙,CSDN记者 ...
- 什么是强化学习,强化学习在控制系统中的应用以及matlab强化学习工具箱的介绍
一.Reinforcement Learning Toolbox介绍 强化学习工具箱使用强化学习算法(包括DQN,A2C和DDPG)为训练策略(policy)提供函数和模块.您可以使用这些策略为复杂的 ...
- 为什么在优化算法中使用指数加权平均
本文知识点: 什么是指数加权平均? 为什么在优化算法中使用指数加权平均? β 如何选择? 1. 什么是指数加权平均 指数加权平均(exponentially weighted averges),也叫指 ...
- 数据分析从零到精通第四课 在产品需求、开发、运营和迭代中进行数据分析
08 数据平台:本机部署大数据平台工具及使用 你好,我是取经儿.上一课时我带你学习了快速搭建 BI 平台来构建报表.本课时我将手把手教你在本地电脑安装离线大数据处理工具 Hive.Hive 也是数据分 ...
最新文章
- Linux的load导入语句,LOAD DATA INFILE语句导入数据进入MySQL的一些注意事项
- 【CV】使用直方图处理进行颜色校正
- linux查看睡眠进程,关于 Linux 进程的睡眠和唤醒 ,来看这篇就够了~
- [渝粤教育] 广东-国家-开放大学21秋期末考试中国近现代史纲要(A)10881k1
- 使用ESP定律_手工脱壳
- java web购物车_java web开发——购物车功能实现
- 笨笨工作室告别十月模拟赛
- 一篇 vSAN 入门,送给大家
- pandas—总结(2) 数据读写 (更新中)
- ylbtech-LanguageSamples-Indexers_2(索引器)
- 信息学奥赛 java C 选_信息学奥赛(NOIP)入门指南(新家长必读)
- 天地图和谷歌地图静态图像素坐标和经纬度坐标互转
- opencv3中的图像混合操作
- 一篇文让你了解JAVA IO(超详细 基础篇)
- 计算机一级wps office考试题库,2017计算机一级WPSOffice复习题(含答案)
- Java/计算银行利率
- VC 使用IE、火狐或默认浏览器打开网页ShellExecute
- Python_文本分析入门_SnowNLP(1)
- 计算机专业前沿算法,CNCC2018 | 研究经典计算机算法已经过时了吗?
- android开机logo和动画修改
热门文章
- FreeNAS虚拟机系统安装
- [ZT]SUN非常有用之 Unix/Linux 单行脚本
- 【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务内容接口卡片接入指南
- 【PHP】PHP基本语法、PHP的数据类型、PHP数据的输出
- 将 bytes 转换成 str
- 淮安计算机教师考编试题及答案,2020年江苏省淮安市市直机关幼儿园幼儿教师招聘/编制考试历年真题试卷及参考答案...
- 看完这篇 Linux 权限后,通透了
- 数据结构(c)的学习总结
- python只能用一行代码_Python 一行代码能实现丧心病狂的功能
- 《数据仓库与数据挖掘教程》ch02数据仓库原理 章节整理