Lua如何实现面向对象

众所周知,Lua作为一种很小却很灵活的脚本语言,如今广泛应用于各种网游的热更新。毕竟是解释型语言嘛,不需要提前编译。我们可以把它打成AssetBundle,现场解压运行。

但是,相信很多做游戏开发的童鞋,从C#等强类型语言转去写Lua代码的时候,可能都感叹过,Lua真难用!!!找个引用要找半天,还经常打错字段,更重要的是,它不支持面向对象啊!我本来就是单身,你还要剥夺我new对象的权利吗?╥﹏╥

不过,随着对Lua的深入♂了解,我发现它还是很迷人的,而且,以上问题前辈们早就有了解决方案。

本文就来谈谈Lua如何实现面向对象。

以C#为例,我是这么理解class的,class是一个数据结构,它将状态(字段)和操作(方法或函数)组合在一个单元中,我们可以使用这个类去动态创建实例(instance),这些实例也被成为对象(object)。

比如,我们定义了一个class bird,

public class Bird
{
    public int color;
    public bool shitOnPeople;

    public void Fly()
    {
    }
}

再通过这个鸟模板new了好几只鸟,这些鸟的字段可能不同,有彩色的鸟,有黑色的鸟,还有些鸟喜欢往人头上拉屎... 但是它们都具有鸟的能力,比如飞翔。

那么,Lua中有没有类似class的东西呢,让我们看看,number不对,string不对,table...这个好像行。那我们来写下试试:

Bird = {}

Bird.color = Color.black
Bird.shitOnPeople = true

Bird.Fly = function() end

好像不太对啊,这样子写出来就是一个实例了。这就是一只白色的,喜欢往人头上拉屎的鸟,而不是泛指鸟类。

那么怎么用lua实现类呢?我们先来思考下C#类的特性:

类支持继承多态性。

我们暂时不管多态,先来看下继承。现在定义一只喜鹊,继承自Bird:

public class Swallow : Bird
{
    public void Sing()
    {
    }
}

虽然我们没写color字段,没写Fly方法,但通过继承的特性,我们用喜鹊还是可以访问到这些东西的。也就是说,如果子类访问自己未定义的方法(或字段),就会去找父类中的方法(或字段)。

等等,好熟悉的感觉,这不就是Lua中的元表吗?!

lua查找表中的元素时规则如下:

 1. 在表中查找,如果找到,返回该元素,找不到则继续
 2. 判断该表是否有元表,如果没有元表,返回nil,有元表则继续
 3. 判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值

完美,我们来试一下。

Bird = {}

Bird.color = white
Bird.shitOnPeople = false

Bird.Fly = function()
    print('i can fly~')
end


Swallow = {}
Swallow.color = black
Swallow.Sing = function()
    print('i can sing~~')
end

setmetatable(Swallow, {__index = Bird})


Swallow.Fly()

运行结果如下:

Lua如何实现面向对象_第1张图片

我们封装一下,不要每次都写setmetatable这句话了,让它变的更像C#。

Class = function(class)
    local proto = {}
    setmetatable(proto, {__index = class})

    proto.New = function()
        return setmetatable({}, {__index = proto})
    end

    return proto
end


Swallow = Class(Bird)
Swallow.color = black
Swallow.Sing = function()
    print('i can sing~~')
end

-- setmetatable(Swallow, {__index = Bird})

Swallow1 = Swallow.New()
Swallow2 = Swallow.New()

Swallow1.Fly()
Swallow2.Sing()

运行一下:

Lua如何实现面向对象_第2张图片

Perfect!

稍微解释下上面一段代码,我们用了两个setmetatable,第一个用在了proto身上,proto就相当于c#中的Class。我们为它增加了一个new方法,在new方法中我们又返回了一个以proto作为元表的空表,这相当于c#中的实例。

不过,这样还不够面向对象,我们还少了构造函数,来加一下吧

Class = function(base)
    local proto = {}
    setmetatable(proto, {__index = base})
    proto.base = base
    proto.New = function(...)
        local obj = setmetatable({}, {__index = proto})
        -- 递归的执行父类构造函数
        local create
        create = function(c, ...)
            if c.base then
                create(c.base,...)
            end
            if c.Ctor then
                c.Ctor(obj,...)
            end
        end

        create(proto, ...)
        return obj
    end

    return proto
end

顺便给proto增加一个__classtype字段,用来实现C#中is的功能:

proto.__classtype = proto

到这里,基本上就达成目的了。

附上源码地址:

https://github.com/787786815/LuaOOP.giticon-default.png?t=M276https://github.com/787786815/LuaOOP.git

你可能感兴趣的:(Lua,lua,开发语言)