美文网首页LuaLUA
Lua实现面向对象机制

Lua实现面向对象机制

作者: _Walker__ | 来源:发表于2017-05-10 11:37 被阅读281次
开篇废话

最近重拾了Lua,因为新项目在用。大概4-5年前准备进入从事游戏行的时候,看到某司的招聘简章中提到了Lua,借暑假的时间看完了《Lua程序设计(第2版)》。还记得开始的时候,看到交换变量用一条语句实现就莫名的兴奋,至今都没搞清楚当初为毛会兴奋...。书中对Lua语言做了比较详细的讲述,强调了其灵活性,然而当时处于似懂非懂的状态,没什么体会。工作一段时间再拿起它,确实产生了共鸣,现在越来越喜欢这小巧的Lua。
  Lua的美在于:它不提供其他语言那么多丰富的特性,但是它提供了自己实现这些特性的机制。
  今天要写的就是借助MetaTable,为Lua添加面向对象的特性。开始之前写上当年为之兴奋的一个行代码~~

a, b = b, a

1 代码

1.1 OO特性实现

此处的实现是从云风大神那里借鉴,简化产生的(Wiki原文)。由于个人资质有限,对稍复杂的方案理解起来比较吃力,虽然原版代码很短,但耗费了挺长时间才理解。为了以后使用方便,根据自己的思维方式,出了这一版自认为比较易于理解的实现。

-- 用于记录所有类结构
local _ClzTbl = {}

-- 索引基类成员的元表
local _BaseMeta = { __index = function(childClz, k)
    if not childClz.base then return nil end
    -- 变种点:1
    -- 将基类中的成员拷贝到子类中,可以提高二次访问的效率(减少搜索的层次)
    local v = childClz.base[k]
    childClz[k] = v
    return v;
end }

--- 定义类结构
--- 约定:构造函数使用名字Ctor
--- 注意:如果指定了基类base,则不能再为子类设置其他MetaTable.
---@param clzName string 类名,必须能全局唯一标识一个类
---@param base table 要继承的基类,没有则不传参
function class(clzName, base)
    if type(clzName) ~= 'string' then return nil end
    local clz = _ClzTbl[clzName]
    -- 类结构已经定义过的,直接返回
    -- 使用这种方式,避免在多次DoFile中被重复定义
    if clz then return clz end
    clz = {
        Ctor = false,
        base = base,
    }
    if base then
        setmetatable(clz, _BaseMeta)
    end
    _ClzTbl[clzName] = clz
    return clz
end

---变种点:2
---递归调用构造函数
---先调用基类的构造函数,再调用自己的
local _Create
_Create = function(clz, obj, ...)
    if clz.base then
        _Create(clz.base, obj, ...)
    end
    if clz.Ctor then
        clz.Ctor(obj, ...)
    end
end

--- 创建对象
---@param clz table 对象所属的类,可以是类型的table也可以是类型的名字
---@param ... 要传递给构造函数的参数列表
function new(clz, ...)
    if type(clz) == 'string' then
        clz = _ClzTbl[clz]
    end
    if not clz then return nil end
    local obj = {}
    setmetatable(obj, { __index = clz })
    _Create(clz, obj, ...)
    return obj
end

1.2 示例

-- 1.定义类
Base = class('Base')
function Base:Ctor(name)
    self._name= name
    print('This is Base....')
end

Child = class('Child', Base)
function Child:Ctor(name)
    print('Old name:' .. self.__name)
    self._name = name
    print('New name:' .. self.__name)
end

-- 2.创建对象(两种方式是等价的)
child1 = new (Child, 'Walker')
child2 = new ('Child', 'Walker')

2 简介

此处实现,沿袭了C#这类本身支持OO的语言的使用习惯,也有Python的影子。Lua中类、对象没有任何区别,都是一个table而已。为了概念上的清晰,这里将其分开,视为不同的东西:class用于定义类结构,new用于创建对象。

2.1 class

用Table作为类结构,也就是生成对象的模板。

  • 直接定义在类上的成员(函数、变量),都是在所有对象间共享的(此处和Python一样)
  • 类结构中,用base变量引用一个基类结构,实现了单继承。
  • 通过__index元方法,实现了高效的基类成员检索(继承)

2.2 new(对象)

  • 直接用元表完成对象结构的创建
  • _Create实现了C#的对象构造顺序(基类->子类)
  • 在构造函数Ctor中对self赋值的变量,才是成员变量(Python方式)。当然由于动态语言的特性,在任意位置给self赋值都有效。

3 变种

细心的朋友可能发现了,实现代码中标记了两处变种点。这一版代码实现的特性和云风大神的基本一致,相当于用一个更易理解的方式重构了(至少对我自己更易理解)。顺带一提,目前项目中使用的就是云风大神的原版。重构完成再看的时候,发现这两处可以用不同的方式,支持其他情况或用法。

3.1 变种点1

变种点1是在调用到基类成员的时候,会将该成员拷贝到子类的Table中。这种方式提高了二次检索的效率,特别是继承层次较深的时候。这是一种空间换时间的方案,当内存资源比CPU资源更稀缺的时候,它就不合适了。并且通常继承层次都不会太深,在我看来Lua中继承超过3层,设计上就很可能有问题了。
  这里的修改方式非常简单,直接__index=base就好了。(文件可以短10行了)

3.2 变种点2

_Create实现的对象构造层次,是从最顶层的基类,依次调用到当前子类,这是C#这类语言中使用的方式。后来想到了Python的方式,是否走基类的构造由子类决定。现在类结构中记录了base,删掉_Create直接调用Ctor就是Python方式。(文件又可以短10多行了Orz....)


粗知漏见,欢迎指正。

相关文章

  • 大话C与Lua(五) 面向对象的数据结构——userdata

    如何实现面向对象? 熟悉Lua的同学都知道!在Lua内部已经实现了面向对象的基本机制(table), 同时也为宿主...

  • Lua实现面向对象机制

    开篇废话 最近重拾了Lua,因为新项目在用。大概4-5年前准备进入从事游戏行的时候,看到某司的招聘简章中提到了Lu...

  • lua实现多继承

    lua对于面向对象的支持主要通过table来实现,每个table都是一个对象,对于继承,lua有元表的机制,通过s...

  • Lua面向对象实现

    这个类主要是把基类和派生类绑定起来,并且调用ctor构造函数用法如下 注意调用父类的方法要用"."别用":"是因为...

  • Lua实现面向对象

    注:只是仿照,并不是真正的面向对象C#中是用this表示当前类的对象,Lua中使用self点(.)与冒号(:)的区...

  • 2018-08-02

    lua实现继承,重载和多态(上) *讲到lua的继承等面向对象的实现,首先得讲一下lua中的几个元方法和元表. s...

  • Lua 实现面向对象 (原创)

    要理解Lua是如何实现面向对象的。首先要熟悉Lua元表的相关知识,可以阅读我上一篇文章《Lua元表 (Metata...

  • lua实现class(面向对象)

    用惯了python,觉得python真的好写。相比起来lua简直是一坨屎。拿lua开发的话,面向对象还是必不可少的...

  • 对lua中类、实例、继承的理解

    lua底层有__index等表,去实现类,实例、继承。但是比较绕。 参考1.1.5.Lua语言面向对象之复制表的方...

  • lua面向对象实现的(记)

    Lua中实现面向对象用的是元表的机制,元表与表是不同的个体,创建table时,不会自动创建元表。不过任何的表都可以...

网友评论

本文标题:Lua实现面向对象机制

本文链接:https://www.haomeiwen.com/subject/ffmwtxtx.html