class - 构建 Lua 类
问题描述
我正在 Lua 中构建一个类,其中包含许多相关函数组,但不确定是否有更好的方法来构建它。我目前必须为 Lua 5.1 环境进行开发,但我希望 Lua 5.3 在不久的将来成为可能。
该类将在许多不同的 Lua 程序中使用,所以我想要一些可以作为单个代码块放入的东西(我正在编程的环境意味着模块和 require 不是也不会是选项)。
理想情况下,我想要一段黑盒代码(公开的公共方法除外),而不是在不同的类中重复代码(以提高可维护性)。
我目前拥有的是(广义的):
function Fclass()
--here I declare a bunch of local functions that can be called by any of the public methods
local function A(parms)
end
--Public methods set 1
--here I declare a bunch of state variables shared by BSelector and GetB
local BSelector = function()
A(parmvalues)
--returns a bunch of iup controls with supporting (complicated) logic
end
local GetB = function()
--returns the values of the iup controls created in Bselector
end
--Public methods set 2
--here I declare a bunch of state variables shared by DSelector and GetD
local DSelector = function()
--returns a bunch of iup controls with supporting (complicated) logic
end
local GetD = function()
A(parmvalues)
--returns the value of the iup controls created in Dselector
end
return{BSelector =BSelector , GetB =GetB, DSelector =DSelector , GetD =GetD}
end
“B”和“D”组方法是完全独立的,除了它们都使用局部函数“A”等(不依赖于外部变量);理想情况下,它们的状态变量应该是该组的本地变量。
这是一个合理的结构吗?或者我应该将“B”和“D”组分成两个单独的类,然后复制本地函数或将它们作为单独的代码放入?我真的不想在类之外公开本地函数,因为不可避免地会发生命名冲突......大多数程序将使用所有方法组,尽管有些程序只使用一个组。
还是有更好的方法来做到这一点?
我这样调用它们:
myB = Fclass()
myD = Fclass()
someresults = myB.Bselector()
otherresults = myD.Dselector()
更新添加:我被告知我可能没有正确使用术语,我正在做的不是课程。我的方法是基于Lua 中的编程并被选中是因为我想保留类的状态变量?目的?私有的——只能通过公共方法访问。
解决方案
在您的示例中,您似乎通过闭包而不是表值封装了实例的状态。
虽然这具有封装性更强的优点,因为在不使用调试库的情况下,upvalue 从外部是不可见的,但它也带来了 Lua 必须关闭每个实例的每个方法,浪费更多内存(虽然不是很多)的缺点。
另一个好处是当实例变量实现为表字段时,它们不需要在方法之前声明,因为表索引是基于字符串的,而当实现为闭包时,需要在定义函数之前知道局部变量(这也适用于其他方法,它们在任一实现中的工作方式与实例变量相同)。
更常见的做法是将实例变量存储为对象内的表值,并将对象作为第一个参数传递给函数。这甚至还有语法糖。
在 Lua 中有很多方法可以做类,有很多不同的权衡(有些更擅长继承,而另一些表现更好,等等)
由于您似乎不需要任何继承,因此您可以使用简单的工厂函数,因为您已经在做很多事情了。
我个人喜欢构建此类工厂函数的方式是:
local object do
local class = {}
local meta = {__index=class} -- Object metatable
function class:print() -- A method of the class
print("Hello, I am an object and my x is " .. tostring(self.x))
end
function object(self) -- The factory function for the Class
self.x = self.x or 0
return setmetatable(self, meta)
end
end
local o = object {
x = 20
}
o:print()
o.x = 30
o:print()
这样做的好处是,对于具有许多方法和许多实例的类,方法不会复制到每个实例中,这样可以节省一些内存。
或者,你可以做这样的事情
local object do
local function object_print(self)
print("Hello, I am an object and my x is " .. tostring(self.x))
end
function object(self)
self.x = self.x or 0
self.print = object_print -- Store method directly in the object
return self
end
end
同样,这会在每个实例中保存对每个方法的引用,从而浪费一些内存。好处是您现在可以将类视为特征。当你写
person { name = "henry" }
您可以将其视为创建一个名为 Henry 的新人,但您也可以将其视为创建一个名为 Henry 的对象并向其添加人员特征。
由于将 OOP 的两个概念结合到一个实现中并且没有任何令人讨厌的继承的好处,在大多数简单的情况下,这是我最喜欢在 Lua 中构建对象的方式。
更新
trait 方法也适用于一起定义多个类/特征:
local person, human do
-- Some generic method shared by both classes
local function object_get_name(self)
return self.name
end
-- Person uses this as a method, but human uses
-- it as a function through an upvalue. Both work,
-- but have different upsides and downsides.
-- A method of person
local function person_say_hi(self)
print(self:get_name() .. " says hi!")
-- Calling get_name as a method here
end
-- A method of human
local function human_describe(self)
print(object_get_name(self) .. ' is a human!')
-- Calling get_name as an upvalue
end
function person(self)
self.name = self.name or 'A person'
self.say_hi = person_say_hi
self.get_name = object_get_name
-- Needs to be a method because person_say_hi assumes it to be one
return self
end
function human(self)
self.name = self.name or 'A human'
self.describe = human_describe
return self
end
end
-- Create a new person
local henry = person{ name = "Henry" }
henry:say_hi()
-- Create a new human
local steve = human { name = "Steve" }
steve:describe()
-- Change the way henry gets his name
function henry:get_name()
return self.name:upper()
end
-- This only affects henry; all other "person" objects keep their old
henry:say_hi()
-- This only works because say_hi accesses the method
-- Add the person trait to steve
person(steve)
steve:describe() -- Steve is still a human
steve:say_hi() -- Steve is also a person now
推荐阅读
- python - keras utils normalize的目的是什么?
- javascript - 将 json 插入另一个 json
- javascript - 使用节点引入 javascript 页面
- php - 动态幻灯片 php 引导程序
- java - 使用 Jfilechooser 编辑仍然打开的文件副本,并在行尾附加 sum
- django - Django 视图 HttpResponseRedirect
- c++ - 我包括了std,但我仍然收到错误,这是为什么呢?
- javascript - javascript--遍历json并在末尾添加累积数
- ocr - 限制 Tesseract 中的空间大小
- game-maker-studio-2 - 低分辨率背景显得模糊