• 关于Lua中的面向对象实现


    写在前面

    • 最近在琢磨“Lua热重载”,在测试中发现我之前对Lua中的面向对象实现有一些理解发生变化,这里记录一下。
    • 本文提到的面向对象实现来自云风

    类实现

    • 《Lua程序设计(第4版)》以银行账户存取钱为例,解释了如何实现一个类。从“面向对象的3大特性”角度,它的实现概括如下:
      • 继承:改__index引向自己
      • 多态:不用新建其他类,会自动找到对应方法(多重继承,书上举例是用查找父类方法实现的)
      • 封装:
        • 单方法:将公开的字段和函数放在单独表中;不过这种方式不能“继承”。
        • 对偶:把表当key存起来;可“继承”。
    • 不过《Lua程序设计(第4版)》书上没有完整的可直接拷走的代码,我工作项目用的是toLua,toLua也没有提供Lua类脚本,所以用了云风设计的class代码,如下:
    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好在哪,之前我在看LuaFramework_UGUI框架时,它有一个写的很短很简单的LuaClass(如下),拿LuaClass和云风的对比,能看得出来LuaClass在“面向对象的3大特性”上没有实现“多态”和“封装”,LuaClass仅能作为参考,是不能直接搬到正式工程里用的。
    --Author : Administrator
    --Date   : 2014/11/25
    
    --声明,这里声明了类名还有属性,并且给出了属性的初始值。
    LuaClass = {x = 0, y = 0}
    
    --这句是重定义元表的索引,就是说有了这句,这个才是一个类。
    LuaClass.__index = LuaClass
    
    --构造体,构造体的名字是随便起的,习惯性改为New()
    function LuaClass:New(x, y) 
        local self = {};    --初始化self,如果没有这句,那么类所建立的对象改变,其他对象都会改变
        setmetatable(self, LuaClass);  --将self的元表设定为Class
        self.x = x;
        self.y = y;
        return self;    --返回自身
    end
    
    --测试打印方法--
    function LuaClass:test() 
        logWarn("x:>" .. self.x .. " y:>" .. self.y);
    end
    
    --endregion
    • 回到class,最开始阅读class代码时,我有好些地方没看懂,包括“_class是放什么的”,“class_type为什么要起名叫class_type”等等。最近在琢磨“Lua热重载”时,再次回顾了这里,对以前理解错误的地方或是一知半解的问题有了新回答。以下是我在阅读时冒出的问题和对应回答:
      • _class是什么?——> _class是记录各种类的类型(以下都称为“类-原型”)的词典(如果是我,会起名为_classTypeMap,这样更好懂)。
        • 比如:新建两个类-原型A和B,此时_class中会记有类-原型A和类-原型B。
    local A = class()
    local B = class()     
      • class()到底做了什么?——> class()其实负责的是创建“类-原型”,而非“类-实例”,这从它最后return的值是class_type可以看出。如果需要创建“类-实例”,就得用new函数。
      • 下面的代码是什么意思?——> vtbl 即value table,setmetatable这里对class_type有写操作,却无读操作,即class_type里的数据是只写属性,即这些数据仅限于类内部使用。
    local vtbl={}
    _class[class_type]=vtbl
    
    setmetatable(class_type,{__newindex=
        function(t,k,v)
            vtbl[k]=v
        end
    })

        有需要的话,可以修改,例如下方就是把原来的只写属性改为可读可写属性,即public:

    local vtbl = {}
    _class[class_type] = vtbl
    
    setmetatable(class_type, {
        __newindex = function(t,k,v)
            vtbl[k] = v
        end,
        
        __index = vtbl,
    })
      • 下面的代码是什么意思?——> 查找自己没有的数据时,如果有父类super,先去_class里找类-原型super的数据,拿该数据覆盖/填充
    if super then
        setmetatable(vtbl,{__index=
            function(t,k)
                local ret=_class[super][k]
                vtbl[k]=ret
                return ret
            end
        })
    end
    • 根据上面回答,以下是我加了注释的class代码:
    -- https://blog.codingnow.com/cloud/LuaOO
    -- 类(注释版)
    
    -- _class是记录各种类的类型(以下都称为“类-原型”)的词典
    -- 比如:local A = class(),local B = class(),则_class中会记有类-原型A和类-原型B
    -- 如果是我,会起名为_classTypeMap,这样更好懂
    local _class = {}
    
    function class(super)
        -- 创建的是类-原型class_type,只有用了类-原型的new函数,才能得到类-实例
        local class_type = {} 
        -- 自定义构造函数ctor    
        class_type.ctor = false
        class_type.super = super
        class_type.new = function(...)
            local obj = {}
            do
                local create = function(c, super)
                    if c.super then
                        create(c.super, ...)                    
                    end
                    if c.ctor then
                        c.ctor(obj, ...)
                    end
                end
                create(class_type, ...)            
            end
            -- 在_class中查找类-类型为class_type的数据
            setmetatable(obj, {__index = _class[class_type]})
            return obj
        end
        
        -- vtbl即value table
        local vtbl = {}
        _class[class_type] = vtbl
        -- 对class_type有写操作,却无读操作,即class_type里的数据是只写属性,即这些数据仅限于类内部使用
        -- (可见《Lua程序程序设计(第4版)》第21章“面向对象编程”的“对偶”)
        setmetatable(class_type, 
            {
                __newindex = function(t, k, v)
                    vtbl[k] = v
                end
                
            })
        
        -- 查找自己没有的数据时,如果有父类super,先去_class里找类-原型super的数据,拿该数据覆盖/填充
        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
  • 相关阅读:
    Cocos2dx 学习笔记(6) 场景对象的移动
    Torque2D MIT 学习笔记(14) 动画资源(AnimationAsset)
    Cocos2dx 学习笔记(4) 对笔记3中触摸控制的第二种实现
    Torque2D MIT 学习笔记(12) 资源基类(AssetBase)
    Torque2D MIT 实战记录: 塔防进度(2)
    Torque2D MIT 学习笔记(17) 如何遍历与查询资源
    Torque2D MIT 实战记录: Isometric(等轴视距)
    Torque2D MIT 脚本阅读(4) ChainToy
    Torque2D MIT 实战记录: 塔防进度(1)
    Torque2D MIT 学习笔记(16) 物理系统(2)
  • 原文地址:https://www.cnblogs.com/caiger-blog/p/14951758.html
Copyright © 2020-2023  润新知