下面是“技术不够,代码来凑”的环节,我只是个搬运工 – 一切尽在代码、注释中
前一篇我们简单的实现了类似 OOP 的 lua 中的 class 设计:lua __call 测试,模拟 OOP 设计
这一篇,我们将更进一步的完善 lua 中 OOP 的代码:
-- 参考:https://blog.youkuaiyun.com/linjf520/article/details/108771189#Custom_splits_ss___233
-- 字符串分割函数
---@param str 要被分割的字符串
---@param split_char 以什么字符分割,注意是:字符
function split(str, split_char)
assert(str ~= nil, "split.str is nil")
assert(split_char ~= nil, "split.split_char is nil")
local ret = {}
for v in string.gmatch(str, "[^" .. split_char .. "]*") do
-- print("v:" .. v)
if v ~= "" then -- pass empty
-- print("match str : " .. v)
table.insert(ret, v)
end
end
return ret
end
-- 注册 命名空间与类信息
function register_ns_cls(names, tbl)
-- print("register_ns_cls(names), names : " .. names)
-- 分割 namespace & class
local ns = split(names, ".")
-- 前置 namespace 层级对象,开始第一个层级的前置父级是 _G
local pre_level_obj = _G
local pre_class = ""
-- 遍历 ns 注册所有 ns 与 class 到 _G
local levels = #ns - 1
for i = 1, levels do
-- 先查看前置对象中是否有该 level 对象
local level = ns[i]
local tbl = pre_level_obj[level]
if tbl == nil then
-- print("not found : " .. pre_class .. "." .. level .. ", and register it")
-- 如果没有,就创建一个
tbl = {}
-- 注册到前置对象中
pre_level_obj[level] = tbl
-- print("_G:" .. tostring(_G) .. ", pre_level_obj:" .. tostring(pre_level_obj) .. ", tbl : " .. tostring( tbl) .. ", pre_level_obj[level] : " .. tostring(pre_level_obj[level]) .. ", _G[level] : " .. tostring(_G[level]))
else
-- 如果有,但又不是 table 类型,那就抛个错误
assert(type(tbl) == "table", "_G[\"" .. level .. "\"] is not a table")
end
pre_class = pre_class .. "." .. level
-- 然后让当前的 tbl 作为前置对象给下一个注册使用
pre_level_obj = tbl
end
local last_one_level_name = ns[#ns]
pre_level_obj[last_one_level_name] = tbl
end
-- 调用带继承层级的构造函数
function _call_ctor_inherit(cls, self, ...)
-- 递归继承的metatable
local _base_cls = getmetatable(cls)
if _base_cls then
print("check inherit ctor, in : " .. _base_cls.__name)
if _base_cls and _base_cls.__index and type(_base_cls.__index) == "table" then
_call_ctor_inherit(_base_cls.__index, self)
end
end
-- 如果有构造函数就调用
-- 这里不要使用 obj.ctor,因为可能会查找到 __index 里面去了,所以使用 rawget 来获取原始对象信息,这就可以绕过 __index 的索引查找
-- local ctor = obj.ctor
local ctor = rawget(cls, "ctor")
if ctor then
print(cls.__name .. " have ctor")
ctor(self, ...)
else
print(cls.__name .. " have no ctor")
end
end
-- 类的构造函数原型定义、函数实体
function cls_new(self, ...)
-- self 是 cls
print(self.__name .. " __call function, with args : ", ...)
local new_obj = { __name = self.__name }
-- 给示例化对象设置类接口元表
setmetatable(new_obj, self)
-- 调用带继承层级的构造函数
_call_ctor_inherit(self, new_obj, ...)
print("return new_obj.namne :" .. new_obj.__name .. ", new_obj : " .. tostring(new_obj))
-- 返回创建的对象
return new_obj
end
-- 模拟 oop 语言 定义一个类,可以指定他的名字字段、和他的基类
function class(name, base_cls)
print("define class, name : " .. name)
if base_cls then
print(name .. " have base_cls : " .. tostring(base_cls.__name))
else
print(name .. " have no base_cls")
end
-- 类信息 table
local cls = {}
-- 类的元表 table
local mtbl = {}
-- 给 cls 设置名字
cls.__name = name
cls.__index = cls
-- 给 元表 设置名字
mtbl.__name = name
-- 将 元表 设置为基类
mtbl.__index = base_cls
-- 设置类似 构造函数的地方
mtbl.__call = function(self, ...) --[=[self 是 cls]=] return cls_new(self, ...) end
-- 将 cls 表信息对象的元表应用
setmetatable(cls, mtbl)
-- 注册 命名空间与类信息
register_ns_cls(name, cls)
-- 注意返回的是一个类
return cls
end
可以看到这回,我们的 class
函数会简洁很多
而且还多了几个辅助函数
function split(str, split_char)
字符串分割函数,前些篇当中有类似的实现:在 lua 中的正则简单使用 - Custom split(s, ss) - 自定义分割函数function register_ns_cls(names, tbl)
将带有namespace
命名空间层级的类信息注册到_G
中function _call_ctor_inherit(cls, self, ...)
调用cls
对一个的构造函数function cls_new(self, ...)
构造函数原型、实体的定义
示例1
下面是应用示例:
function define_human_and_tom_cls()
local HumanCls = class("HumanCls")
-- 给 HumanCls 添加 ctor 构造函数
HumanCls.ctor = function(self, ...)
print("HumanCls.ctor, inst.__name : " .. self.__name)
end
local TomCls = class("TomCls", HumanCls)
-- 给 TomCls 添加 ctor 构造函数
TomCls.ctor = function(self, ...)
print("TomCls.ctor, inst.__name : " .. self.__name)
end
end
function test_new_tom()
define_human_and_tom_cls()
if TomCls == nil then
print("not found TomCls in _G")
TomCls = _G["TomCls"]
else
if TomCls.__call then
print("TomCls found the new()")
else
print("TomCls not found the new()")
end
end
local tom = TomCls("arg1", "arg2")
print("tom.name : " .. tostring(tom.__name) .. ", tom : " .. tostring(tom))
end
print("=== test_new_tom() ===")
test_new_tom()
输出:
=== test_new_tom() ===
define class, name : HumanCls
HumanCls have no base_cls
define class, name : TomCls
TomCls have base_cls : HumanCls
TomCls not found the new()
TomCls __call function, with args : arg1 arg2
check inherit ctor, in : TomCls
check inherit ctor, in : HumanCls
HumanCls have ctor
HumanCls.ctor, inst.__name : TomCls
TomCls have ctor
TomCls.ctor, inst.__name : TomCls
return new_obj.namne :TomCls, new_obj : table: 00B09570
tom.name : TomCls, tom : table: 00B09570
示例2
function test_more_cls()
-- 下面我们可以把继承层级弄深一些,并且将 .ctor 构造函数值设置一部分,再看看输出信息
function define_BASE_AND_ABCD_cls()
end
local BASE_CLS = class("Base_CLS")
local CLS_A = class("CLS_A", BASE_CLS)
local CLS_B = class("CLS_B", CLS_A)
local CLS_C = class("CLS_C", CLS_B)
local CLS_D = class("CLS_D", CLS_C)
-- CLS_A、CLS_D 都不设置构造函数
CLS_B.ctor = function(self)
print("CLS_B.ctor run, inst.name : " .. self.__name)
end
CLS_C.ctor = function(self)
print("CLS_C.ctor run, inst.name : " .. self.__name)
end
local new_d = CLS_D("arg1", "arg2", "arg3")
print("new_d.cls__name : " .. new_d.__name)
end
print("=== test_more_cls() ===")
test_more_cls()
输出:
=== test_more_cls() ===
define class, name : Base_CLS
Base_CLS have no base_cls
define class, name : CLS_A
CLS_A have base_cls : Base_CLS
define class, name : CLS_B
CLS_B have base_cls : CLS_A
define class, name : CLS_C
CLS_C have base_cls : CLS_B
define class, name : CLS_D
CLS_D have base_cls : CLS_C
CLS_D __call function, with args : arg1 arg2 arg3
check inherit ctor, in : CLS_D
check inherit ctor, in : CLS_C
check inherit ctor, in : CLS_B
check inherit ctor, in : CLS_A
check inherit ctor, in : Base_CLS
Base_CLS have no ctor
CLS_A have no ctor
CLS_B have ctor
CLS_B.ctor run, inst.name : CLS_D
CLS_C have ctor
CLS_C.ctor run, inst.name : CLS_D
CLS_D have no ctor
return new_obj.namne :CLS_D, new_obj : table: 00E5C170
new_d.cls__name : CLS_D
示例3
function test_has_inherit_ns_cls()
-- 下面注册类型 C# 中的泛型类信息
print("###### start testing c#-like generic defines ######")
local System = class("System")
local Generic = class("System.Generic")
local List = class("System.Generic.List")
local Array = class("System.Generic.Array")
local Stack = class("System.Generic.Stack")
local Queue = class("System.Generic.Queue")
-- 下面是注册我自己测试的类信息
print("###### start testing my ns level & cls defines ######")
local MyNs1 = class("MyNs1")
local MyNs2 = class("MyNs1.MyNs2")
local Custom = class("MyNs1.MyNs2.Custom")
-- 下面是直接跨层级注册
print("###### start testing straghtly levels & cls defines ######")
local MyCustomClass = class("Level1.Level2.Level3.MyCustomClass")
end
function applying_custom_inherit_cls()
print("###### start testing new levels & cls defines ######")
local queue_inst = System.Generic.Queue("QueueArg1", "QueueArg2", "QueueArg3")
local custom_inst = MyNs1.MyNs2.Custom("CustomArg1", "CustomArg2", "CustomArg3")
local my_custom_inst = Level1.Level2.Level3.MyCustomClass("MyCustomArg1", "MyCustomArg2", "MyCustomArg3")
print("queue_inst.__name : " .. tostring(queue_inst.__name))
print("custom_inst.__name : " .. tostring(custom_inst.__name))
print("my_custom_inst.__name : " .. tostring(my_custom_inst.__name))
end
print("=== test_has_inherit_ns_cls() ===")
test_has_inherit_ns_cls()
applying_custom_inherit_cls()
输出:
=== test_has_inherit_ns_cls() ===
###### start testing c#-like generic defines ######
define class, name : System
System have no base_cls
define class, name : System.Generic
System.Generic have no base_cls
define class, name : System.Generic.List
System.Generic.List have no base_cls
define class, name : System.Generic.Array
System.Generic.Array have no base_cls
define class, name : System.Generic.Stack
System.Generic.Stack have no base_cls
define class, name : System.Generic.Queue
System.Generic.Queue have no base_cls
###### start testing my ns level & cls defines ######
define class, name : MyNs1
MyNs1 have no base_cls
define class, name : MyNs1.MyNs2
MyNs1.MyNs2 have no base_cls
define class, name : MyNs1.MyNs2.Custom
MyNs1.MyNs2.Custom have no base_cls
###### start testing straghtly levels & cls defines ######
define class, name : Level1.Level2.Level3.MyCustomClass
Level1.Level2.Level3.MyCustomClass have no base_cls
###### start testing new levels & cls defines ######
System.Generic.Queue __call function, with args : QueueArg1 QueueArg2 QueueArg3
check inherit ctor, in : System.Generic.Queue
System.Generic.Queue have no ctor
return new_obj.namne :System.Generic.Queue, new_obj : table: 00E64758
MyNs1.MyNs2.Custom __call function, with args : CustomArg1 CustomArg2 CustomArg3
check inherit ctor, in : MyNs1.MyNs2.Custom
MyNs1.MyNs2.Custom have no ctor
return new_obj.namne :MyNs1.MyNs2.Custom, new_obj : table: 00E64550
Level1.Level2.Level3.MyCustomClass __call function, with args : MyCustomArg1 MyCustomArg2 MyCustomArg3
check inherit ctor, in : Level1.Level2.Level3.MyCustomClass
Level1.Level2.Level3.MyCustomClass have no ctor
return new_obj.namne :Level1.Level2.Level3.MyCustomClass, new_obj : table: 00E645A0
queue_inst.__name : System.Generic.Queue
custom_inst.__name : MyNs1.MyNs2.Custom
my_custom_inst.__name : Level1.Level2.Level3.MyCustomClass