Class 构造Lua类

Class 构造Lua类,class,lua,scope,Class,Lua,Scope,我正在用Lua构建一个类,其中包含许多相关函数组,但不确定是否有更好的方法来构造它。我目前必须开发Lua5.1环境,但我希望Lua5.3在不久的将来成为可能 该类将在许多不同的Lua程序中使用,因此我想要一些可以作为单个代码块插入的东西(我为之编程的环境意味着模块和需求不是也不会是一个选项) 理想情况下,我需要一段黑盒代码(公开的公共方法除外),并且不要在不同的类中复制代码(以提高可维护性) 我目前拥有的是(概括而言): “B”和“D”组方法是完全独立的,除了它们都使用局部函数“A”等(不依赖于

我正在用Lua构建一个类,其中包含许多相关函数组,但不确定是否有更好的方法来构造它。我目前必须开发Lua5.1环境,但我希望Lua5.3在不久的将来成为可能

该类将在许多不同的Lua程序中使用,因此我想要一些可以作为单个代码块插入的东西(我为之编程的环境意味着模块和需求不是也不会是一个选项)

理想情况下,我需要一段黑盒代码(公开的公共方法除外),并且不要在不同的类中复制代码(以提高可维护性)

我目前拥有的是(概括而言):

“B”和“D”组方法是完全独立的,除了它们都使用局部函数“A”等(不依赖于外部变量);理想情况下,它们的状态变量应该是组的局部变量

这是一个合理的结构吗?或者我应该将“B”和“D”组拆分为两个单独的类,或者复制本地函数,或者将它们作为单独的代码段放入?我真的不想公开类之外的本地函数,因为不可避免地会有命名冲突。。。大多数程序将使用所有的方法组,尽管有些程序只使用一个组

还是有更好的方法

我这样引用它们:

myB = Fclass()
myD = Fclass()
someresults = myB.Bselector()
otherresults = myD.Dselector()

更新补充:我被建议可能没有正确使用术语,我所做的不是课程。我的方法基于,之所以选择它是因为我想保留类的状态变量?对象private——除非通过public方法,否则无法访问。

在您的示例中,似乎是通过闭包而不是表值来封装实例的状态

虽然这样做的优点是封装性更强,因为upvalue在不使用调试库的情况下从外部是不可见的,但它也带来了缺点,即Lua必须为每个实例关闭每个方法,从而浪费更多的内存(虽然不是很多)

另一个好处是,当实例变量实现为表字段时,它们不需要在方法之前声明,因为表索引是基于字符串的,而当实现为闭包时,在定义函数之前需要知道局部变量(这也适用于其他方法,它们在两种实现中的工作方式与实例变量相同)

更常见的做法是将实例变量存储为对象内的表值,并将对象作为第一个参数传递给函数


在Lua中有很多方法可以实现类,有许多不同的折衷(一些方法在继承方面更好,而另一些方法性能更好,等等)

因为您似乎不需要任何继承,所以可以使用一个简单的工厂函数,就像您已经做的那样

我个人喜欢构建此类工厂功能的方式是:

localobjectdo
本地类={}
局部元={uuuu index=class}--对象元表
函数类:print()--该类的方法
打印(“你好,我是一个对象,我的x是”。.tostring(self.x))
结束
函数对象(self)——类的工厂函数
self.x=self.x或0
返回可设置元表(self,meta)
结束
结束
局部o=对象{
x=20
}
o:print()
o、 x=30
o:print()
这样做的好处是,对于具有许多方法和实例的类,方法不会复制到每个实例中,从而节省了一些内存

或者,您也可以这样做

localobjectdo
本地功能对象打印(自身)
打印(“你好,我是一个对象,我的x是”。.tostring(self.x))
结束
函数对象(自身)
self.x=self.x或0
self.print=object\u print——将方法直接存储在对象中
回归自我
结束
结束
同样,这会在每个实例中保存对每个方法的引用,从而浪费一些内存。好处是您现在可以将类视为特征

person{name=“henry”}
你可以把它想象成创建一个名为Henry的新人,但你也可以把它想象成创建一个名为Henry的对象,并将person特征添加到其中

由于将OOP的两个概念结合到一个实现中并且没有任何讨厌的继承,在大多数简单的情况下,这是我最喜欢的在Lua中构建对象的方法


更新 特质方法也有助于同时定义几个类/特质:

本地人,人类
--两个类共享的一些泛型方法
本地函数对象\u获取\u名称(self)
返回self.name
结束
--人用它作为一种方法,但人用它
--它作为一个函数通过一个向上的值。两者都起作用,
--但有不同的正面和负面。
--人的方法
本地功能人员(自我)
打印(self:get_name()..“说你好!”)
--在此处调用get_name作为方法
结束
--一种人性化的方法
局部功能人类描述(自我)
打印(对象_get_name(self)…'是人类!')
--将get_name作为upvalue调用
结束
功能人(自我)
self.name=self.name或“一个人”
self.say\u hi=人
self.get\u name=object\u get\u name
--需要是一种方法,因为person\u say\u hi假设它是一种方法
回归自我
结束
功能人(自我)
self.name=self.name或“人”
self.descripe=人的描述
回归自我
结束
结束
--创造一个新的人
本地亨利=人{name=“henry”}
亨利:你好
--创造一个新的人类
本地史蒂夫=人类{name=“史蒂夫”}
史蒂夫:描述一下
--改变亨利取名字的方式
函数henry:get_name()
返回self.name:upper()
结束
--这只会影响henry;所有其他“person”对象都会保持其原有状态
亨利:您好
myB = Fclass()
myD = Fclass()
someresults = myB.Bselector()
otherresults = myD.Dselector()
function class(first, second)

    local public = {first}
    local _private = {second}

    function _private.A(parms)
        --private function not available outside of class.
    end

    function public:selector() -- public class available to caller
        _private.A(parmvalues) -- calls private class
    end

    function public:get()
        return _private[1]
    end

    return public
end

myB = class('hello', ' world!') --passing in a variable for public, and one for private.
myD = class('hello...', ' world?')

print(myB[1] .. myB:get()) --get a public value, and uses get function to retrieve private value
print(myD[1] .. myD:get())
    local meta = {
        __index = public,
        __newindex = function(t, k, v)
                error("this table is read-only")
              end,
        __metatable = false
    }

    return setmetatable({}, meta) -- this make the public table read only