Lua实现面向对象

前文

市面上已经有非常多的lua实现面向对象的相关文章了,本文也不希望再重复赘述该怎么实现一个面向对象。而是记录下自己实现面向对象的全部流程,以及踩到的坑点和对应的解决的方法,本文将一直更新下去,直到实现个人认为最完美的Lua面向对象为止。

第一版

function class(classname, super)
    local cls = { __name = classname, __super = super }

    if super then
        setmetatable(cls, { __index = super })
    end

    function cls:new(...)
        -- 创建新实例
        local obj = {}
        setmetatable(obj, {__index = self})

        -- 调用构造方法
        if self.ctor then
            self.ctor(obj, ...)
        end
        return obj
    end

    return cls
end
  1. 新生成一张表cls(可以理解成CLR的类型对象),给cls添加一个元表,元方法__index 赋值为 super(父类型对象),在cls中找不到的key,会到super中去寻找。
  2. 给cls新增一个方法new(),该方法返回cls类型对象的一个实例,并主动调用构造方法ctor,初始化实例对象中的成员
-- 使用方法展示
local Parent = class("Parent", Grandfather)
function Parent:ctor(name)
    self.parentName = name
end

local Child = class("Child", Parent)
function Child:ctor(name)
    self.__super:ctor(name)
    self.childName = name
end

local A = Child:new('A')
local B = Child:new('B')
print(A.childName)  -- 输出:A
print(B.childName)  -- 输出:B

可以看到,A和B已经是2个不同的实例对象,我们给A和B的childname进行不同的赋值,最后输出的结果也是不相同的赋值,是正确的。然后我们尝试输出下父类中的parentName值试试,看看有没有什么不同:

print(A.parentName)  -- 输出:B
print(B.parentName)  -- 输出:B

我去,异常的情况发生了,可以看到B实例“修改”了A实例的parentName,那么这个是怎么造成的呢,我们来分析一下:
image
我们在对Child实例化时,调用了self.__super:ctor(name),其中的self.__super其实是Parent类型对象,那么我们修改parentName时,其实修改的是Parent类型对象中的值,而非Child实例的值。【说的比较拗口,但懂的人自然懂了。如果读者不明白类型对象和类型对象实例的区别,请自行查阅一下】
这也是为什么2个实例都输出了“B”,那么我们该怎么解决这个问题呢?
我们回想下CLR在实例化一个对象的时候,它做了什么?它会计算类型及其所有基类型(一直到System.Object)中定义的所有实例字段需要的字节数。在托管堆上分配所需的空间,最后对所有实例字段初始化。我们这里也正是少了在实例中分配基类型实例字段空间的过程,由此,我衍生出第二版来修复这个问题。

第二版

function class(classname, super)
    local cls = { __name = classname, __super = super }

    -- 继承父类方法
    if super then
        setmetatable(cls, { __index = super })
    end

    -- 实例的元表指向类
    cls.__index = cls

    -- 构造函数(自动克隆类字段)
    function cls:new(...)
        local obj = {}
        setmetatable(obj, self)

        -- 链式调用构造函数
        local function call_ctor(class, ...)
            if class.__super then
                call_ctor(class.__super, ...)
            end
            if rawget(class, "ctor") then
                class.ctor(obj, ...)
            end
        end
        call_ctor(self, ...)

        return obj
    end

    return cls
end
  1. 新生成一张表cls(可以理解成CLR的类型对象),给cls添加一个元表,元方法__index 赋值为 super(父类型对象),在cls中找不到的key,会到super中去寻找。
  2. 给cls新增一个方法new(),该方法返回cls类型对象的一个实例。
  3. new()方法中会自低向上的调用构造函数,并将实例传递进行,进行实例初始化
-- 使用方法展示
local Parent = class("Parent", Grandfather)
Parent.defaultParentName = "defaultParentName"
function Parent:ctor(name)
    self.parentName = name
end

local Child = class("Child", Parent)
function Child:ctor(name)
    self.__super:ctor(name)
    self.childName = name
end

local A = Child:new('A')
local B = Child:new('B')
print(A.childName)  -- 输出:A
print(B.childName)  -- 输出:B
print(A.parentName)  -- 输出:A
print(B.parentName)  -- 输出:B

此时我们可以看到,无论是childName还是parentName都输出了正确的结果。

posted @ 2025-03-05 16:08  陈侠云  阅读(140)  评论(0)    收藏  举报
//雪花飘落效果