刚刚从C#和Java等强类型语言过渡到Lua等弱类型时,总觉得开发过程中有些别扭,而且面向对象的开发过程确实对开发过程中的业务逻辑更方便理解。为了方面广大开发者的习惯,Lua官方也给出了解决方案。
People = {name = "People", age = 12}
--这句是重定义元表的索引,就是说有了这句,这个才是一个类。
People.__index = People
--构造体,构造体的名字是随便起的,习惯性改为New()
function People:New(x, y)
local obj= {} --初始化self,如果没有这句,那么类所建立的对象改变,其他对象都会改变
setmetatable(obj, self) --将self的元表设定为Class
self.name = x
self.age = y
return self --返回自身
end
--测试打印方法--
function People:test()
print("x:>" .. self.name .. " y:>" .. self.age)
end
Lua中的 setmetatable (tableA, { __index = tableB }) 可以将 tableB 设置为 tableA 的原表,那么当 获取tableA中不存在的字段的时候,会去查找元表tableB中是否含有当前字段,有则返回数值。因此,我们可以通过上述代码模拟一下类。
当涉及当类的继承关系时候,应该怎么解决呢?
解决方法一:通过元表一层一层的指向父类,获取字段的时候一层一层的查找。
解决方法二:通过元表指向上一层父类的时候,将父类所有的字段Clone到当前元表中。
解决方法三:通过元表指向父类,并且当获取字段的时候将字段Clone到当前元表中,下次获取字段就可以直接获取数据。
云风大神给出了一个完美解决方案:解决方法三
--[[
模拟继承结构的基类
]]
local _class = {}
function Class(super)
local class_type = {}
class_type.ctor = false
class_type.super = super
class_type.New = function(...)
local obj = {}
do
local create
create = function(c, ...)
if c.super then
create(c.super, ...)
end
if c.ctor then
c.ctor(obj, ...)
end
end
create(class_type, ...)
end
setmetatable(obj, {__index = _class[class_type]})
return obj
end
local vtbl = {}
_class[class_type] = vtbl
setmetatable(
class_type,
{
__newindex = function(t, k, v)
vtbl[k] = v
end,
--For call parent method
__index = vtbl
}
)
if super then
setmetatable(
vtbl,
{
__index = function(t, k)
local ret = _class[super][k]
vtbl[k] = ret
return ret
end
}
)
end
return class_type
end
简单的对代码做一下分析:
- local People=Class(),返回一个Table(People)的内存指针指向class_type{},设置 class_type{} 的元表为 vtbl{} ,并在全局唯一变量_class中记录class_type的内存指针指向vtbl{} 。
- vtbl{} 设置 __newindex = function(t, k, v) ,当给tableA添加属性和方法的时候,调用 __newindex 指向的方法 function(t, k, v),这样 vtbl{} 中相当于存放了 People 的字段和方法。
- 实例化People的时候,需要调用PeopleInstance = People.New()方法,通过该方法返回Table(PeopleInstance)的内存指针指向obj {},设置 obj{} 的元表为_class[class_type],即为 vtbl{},因为 vtbl{} 中存放了 People 的字段和方法,PeopleInstance 即可获取到 People 的字段和方法。
- local Teacher=Class(People) , 类的创建过程与上述相同,当涉及当继承关系时候,Teacher的元表 vtbl{} 会指向 function(t, k) 方法,当获取Teacher中不存在的字段时候,会去_class[super]也就是People的元表vtbl{}中取值,同时给Teacher的元表 vtbl{} 赋值。
总结一下Lua类的封装:
- 官方给出的示例适用于没有继承关系的类,定义简单,使用过程也清楚明了。
- 云风的封装适用于有多层继承关系的情况,使用简单,性能和内存优化上都有较好解决。