Lua的OOP实现

最近一直没在偷懒,在收集各种开源项目中OOP的实现方案。关于其中的理解,因为给新人指导,就要求自己能够很好的理解,并讲解出来。教会也是给对于知识理解的更高阶段。

一、实现OOP

二、lua 的作者推荐了一种方案来实现 OO

function class(def)
    local class = {}
    local parents = {}

    local upv
    local env = _G

    local wraps
    local function super(parent_class)
        if not parent_class then
            parent_class = parents[1]
        end

        local this = this
        local that = {}
        for k, v in pairs(parent_class) do
            that[k] = type(v) == 'function' and wraps(this, v) or v
        end

        return setmetatable(that, that)
    end

    function wraps(this, func)
        return function(...)
            local t = env.this
            local s = env.super

            env.this = this
            env.super = super

            local ret = pcall(func, ...)

            env.this = t
            env.super = s

            return ret
        end
    end

    function class.__init()
    end

    for i = 1, math.huge do
        inherit, v = debug.getlocal(def, i)
        if not inherit then
            break
        end

        local parent_class = _G[inherit]
        for i = 1, math.huge do
            local name, pclass = debug.getlocal(2, i, 1)
            if not name then
                break
            elseif name == inherit then
                parent_class = pclass
                break
            end
        end

        if parent_class and type(parent_class) == 'table' then
            table.insert(parents, parent_class)
            for k, v in pairs(parent_class) do
                class[k] = v
            end
        else
            error(string.format('Class "%s" not valid.', name))
        end
    end

    for i = 1, math.huge do
        local name, value = debug.getupvalue(def, i)
        if not name then
            break
        elseif name == '_ENV' then
            env = value
            upv = i
            break
        end
    end

    local _env = setmetatable({}, {
        __index = function(t, name)
            local value = class[name]
            return value ~= nil and value or env[name]
        end,
        __newindex = function(t, name, value)
            class[name] = value
        end
    })

    local function senv(env)
        if upv then
            debug.setupvalue(def, upv, env)
        else
            _G = env
        end
    end

    senv(_env)
    env.pcall(def, env.table.unpack(parents))
    senv(env)

    return setmetatable({}, {
        __ipairs = function()
            return ipairs(class)
        end,
        __pairs = function()
            return pairs(class)
        end,
        __index = function(t, name)
            return class[name]
        end,
        __index_new = function(t, name, value)
            class[name] = value
        end,
        __call = function(...)
            local this = {}
            for k, v in pairs(class) do
                this[k] = type(v) == 'function' and wraps(this, v) or v
            end
            this.__class = class
            this.__init(...)

            return setmetatable(this, this)
        end
    })
end
--Example:
global = true
Inherit = class(function()
    this_is_a_property_of_Inherit = true

    function __init()
        print('Inherit().__init()')
        this.init = true
    end

    function __call()
        print('Yay! You\'re calling for me :) init:', this.init, '\n')
    end
end)

Example = class(function(Inherit)
    property = "Class property"
    print('Inherited property:', this_is_a_property_of_Inherit)
    print('Global variable:   ', global, '\n')
    function __init()
        print('Example().__init()')
        super().__init()
        print('this.init:', this.init)
    end

    function test(...)
        print(..., this.__init, '\n')
    end
end)

example = Example()
example.test('__init:')
--example()
--
----example.property = 'I\'m a property of instance "example"'
--print('example.property', example.property)
--print('Example.property', Example.property)

SimpleLuaClasses
这个实现方式很特殊,不是那么直白的使用 metatable 去实现,而是通过 debug API 实现的。熟悉 lua 的朋友可能看得出来这个类似与 setfenv,因为只有 lua 5.1 才有 setfenv 和 getfenv,这个是 lua5.3 上的实现。关于 5.3 上的实现可以参考

https://leafo.net/guides/setfenv-in-lua52-and-above.html

三、云风实现的Lua面向对象

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
    })

    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

以上是基本的 class 定义的语法,完全兼容 lua 的编程习惯。增加了一个叫做 ctor 的词,作为构造函数的名字。
https://blog.codingnow.com/cloud/LuaOO

四、cocos2dx-lua 中的 class 实现

function class(classname, super)
    local superType = type(super)
    local cls

    if superType ~= "function" and superType ~= "table" then
        superType = nil
        super = nil
    end

    if superType == "function" or (super and super.__ctype == 1) then
        -- inherited from native C++ Object
        cls = {}

        if superType == "table" then
            -- copy fields from super
            for k,v in pairs(super) do cls[k] = v end
            cls.__create = super.__create
            cls.super    = super
        else
            cls.__create = super
            cls.ctor = function() end
        end

        cls.__cname = classname
        cls.__ctype = 1

        function cls.new(...)
            local instance = cls.__create(...)
            -- copy fields from class to native object
            for k,v in pairs(cls) do instance[k] = v end
            instance.class = cls
            instance:ctor(...)
            return instance
        end

    else
        -- inherited from Lua Object
        if super then
            cls = {}
            setmetatable(cls, {__index = super})
            cls.super = super
        else
            cls = {ctor = function() end}
        end

        cls.__cname = classname
        cls.__ctype = 2 -- lua
        cls.__index = cls

        function cls.new(...)
            local instance = setmetatable({}, cls)
            instance.class = cls
            instance:ctor(...)
            return instance
        end
    end

    return cls
end

来源于chukong的quick-cocos2d-x

五、xlua-framework 中的 class 实现

-[[
-- added by wsh @ 2017-11-30
-- Lua面向对象设计
--]]

--保存类类型的虚表
local _class = {}

-- added by wsh @ 2017-12-09
-- 自定义类型
ClassType = {
    class = 1,
    instance = 2,
}

function BaseClass(classname, super)
    assert(type(classname) == "string" and #classname > 0)
    -- 生成一个类类型
    local class_type = {}

    -- 在创建对象的时候自动调用
    class_type.__init = false
    class_type.__delete = false
    class_type.__cname = classname
    class_type.__ctype = ClassType.class

    class_type.super = super
    class_type.New = function(...)
        -- 生成一个类对象
        local obj = {}
        obj._class_type = class_type
        obj.__ctype = ClassType.instance

        -- 在初始化之前注册基类方法
        setmetatable(obj, {
            __index = _class[class_type],
        })
        -- 调用初始化方法
        do
            local create
            create = function(c, ...)
                if c.super then
                    create(c.super, ...)
                end
                if c.__init then
                    c.__init(obj, ...)
                end
            end

            create(class_type, ...)
        end

        -- 注册一个delete方法
        obj.Delete = function(self)
            local now_super = self._class_type
            while now_super ~= nil do    
                if now_super.__delete then
                    now_super.__delete(self)
                end
                now_super = now_super.super
            end
        end

        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]
                --do not do accept, make hot update work right!
                --vtbl[k] = ret
                return ret
            end
        })
    end

    return class_type
end

xlua-framework
xlua-framework/Assets/LuaScripts/Framework/Common/BaseClass.lua

六、2种实现方式的总结

  • 1、使用metatable的__index域实现,实现的时候需要利用的lua语言的一些特性才可以实现
    • 优点:
      • 1、由于子类的很多数据和方法都是共用了父类的,用到父类的数据和方法的时候,只是在使用的时候才直接调用父类的方法和数据,这样可以减少程序内存的消耗,更主要的是,父类在运行期的修改是会影响到子类的;
      • 2、充分利用了lua语言的特性,父类的方法和数据的访问是解析器来做的,所以效率上的开销还是比较小的;
    • 缺点:
      • 1、如果父类中有一个数据是一个引用的时候(如table)的时候,就会出现在一个子类中操作这个table会改变其他子类的情况,造数据的不一致,所以应该尽量避免这种类的创建,如果有这样的需求的话,就需要对inherit和new函数进行一些特殊的操作,简单来说就是加一个init函数,将所有这类的数据都显示的创建一下。
      • 2、由于每次取操作都需要在metatable中取,所以,每次就会增加一层继承,就增加一个函数调用的开销,虽然是由解析器来做的,但是如果层次多了的话,还是有开销的;
  • 2、使用table拷贝的方式实现,实现的时候利用的lua的技术为:
    • 优点:
      • 1、父类中的数据是全部拷贝到子类中的,所以,不存在数据不一致的情况;
      • 2、所有的函数调用和数据调用都是直接调用每个实例的,不需要到父类中查找;
    • 缺点:
      • 1、全部数据的copy,在创建的时候就会增加一个table copy的过程,影响效率;
      • 2、全部数据和方法都是在创建的时候拷贝一份的,会增加很多的内存消耗,而且如果在运行期改变了父类,并不能改变子类;

七、最后的总结

结合这两种方式的有缺点,从一个面向对象的角度来说,第一种方式更加适合实现面向对象的特性,第二种方式对面向对象的模拟就牵强一些(缺点2),但是从使用的角度来说,因为在访问数据和方法速度上,第二种方式还是有优势的,所以,在具体的使用的时候,可以灵活一下使用,将两者结合一下。

比如说,对于客户端这边来说,类在开始创建好了以后就一般不需要修改了,而且子类一般都是需要父类的所有的方法和数据的,所有我们就可以使用第二种方式,而生成对象实例的时候,对象的实例一般都不会调用类的所有的方法,而且用完了这个实例,就会销毁的,所以,我们这里就可以采用第一种方式。

  • 基于以上的想法进行改进,这样用于作为所有界面的基类
function class(classname,super)
    local superType = type(super)
    local cls
    if superType ~= "function" and superType ~= "table" then
        superType  = nil
        super = nil
    end

    if superType == "function" or (super and super.__ctype == 1) then
        cls = {}

        if superType == "table"  then
            for k,v in pairs(super) do cls[k] = v end
            cls.__create = super.__create
            cls.super = super
        else
            cls.__create = super
        end

        cls.ctor = function(...) end
        cls.__cname = classname
        cls.__ctype = 1

        function cls.New(...)
            local  ins = cls.__create(...)
            for k,v in pairs(cls) do ins[k] = v end
            ins.class = cls
            ins:ctor(...)
            return ins
        end
    else
        if super then
            cls = clone(super) --与cocos2dx-lua 中的 class 实现不同之处
            cls.super = super
        else
            cls = {ctor = function(...) end}
        end
        cls.__cname = classname
        cls.__ctype = 2-- luatable
        cls.__index = cls

        function cls.New(...)
            local ins = setmetatable({},cls)
            ins.class = cls
            ins:ctor(...)
            return ins
        end
    end
    return cls
  • 所有单例的实现就用第五种的实现

  • 感谢这篇文章的作者Lua class 的几种实现,不然我这篇文章可能要拖一段时间
  • http://www.lua.org/pil/16.html
  • lua实现面向对象的特性
  • 感谢Misaka Mikoto[https://www.zhihu.com/people/jasonfeeling]在Lua上解答了我很多问题,给予方向上的指点
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值