在 Lua 中定义类型的简单方法
我通常用 Lua 定义一个类型只需要这样做:
-- 定义一个 object 的新类型
local object = {}; object.__index = object
-- 定义构建 object 的函数
local function new_object(self)
return setmetatable(self or {}, object)
end
-- 给 object 添加一个 get 方法
function object:get(what)
return self[what]
end
-- 测试一下
local obj = new_object { x = "x" }
assert(obj:get "x" == "x")
这样写足够简单,如果写熟了就不用额外再做封装。如果一定要做一点封装,可以这样:
local class = {}; setmetatable(class, class)
function class:__index(name)
local class_methods = {}; class_methods.__index = class_methods
local class_object = {}
local class_meta = {
__newindex = class_methods,
__index = class_methods,
__call = function(self, init)
return setmetatable(init or {}, class_methods)
end
}
class[name] = setmetatable(class_object, class_meta)
return class_object
end
封装的意义在于:你可以通过上面这个 class 模块定义新的类型,且能通过它用类型名找到所有定义的新类型。而上面的第一版通常用于放在独立模块文件中,依赖 lua 的模块机制找到 new_object 这个构建方法。
而封装后可以这样用:
-- 定义一个名为 object 的新类型,并添加 get 方法:
local object = class.object
function object:get(what)
return self[what]
end
-- 创建新的 object 实例,测试方法 object:get
local obj = class.object { x = "x" }
assert(obj:get "x" == "x")
如果觉得 local object = class.object 的写法容易产生歧义,也可以加一点小技巧(同时提供特殊的代码文本模式,方便日后搜索代码):
function class:__call(name)
return self[name]
end
-- 等价于 local object = class.object
local object = class "object"
如果我们要定义的类型是一个容器该怎么做好?
容器的数据结构有两个部分:容纳数据的集合和容器的元数据。之前,我通常把元数据直接放在对象实例中,把集合对象看作元数据中的一个。
比如定义一个集合类型 set 以及两个方法 get 和 set :
local set = class "set"
function set:new()
return self {
container = {},
n = 0,
}
end
function set:set(key, value...剩余内容已隐藏