Cocos2d+Lua 游戏开发中的奇技淫巧系列之二 Lua 面向对象编程(OOP)的实现

        众所周知,lua在语法层面是不提供class关键字的,lua要实现面向对象编程(OOP)里面的继承,通常要用table和元表来实现。包括官方教程和网络上都有很多关于如何用lua实现面向对象编程的介绍。因为我觉得class的实现已经比较完善了,所以这篇文章主要介绍是介绍别人的class实现,再加上自己的一些点评,没有太多原创,好了,不废话,开始。

      首先说一下面向对象编程(OOP)的三大特性:封装,继承,多态。

一、封装就是对数据成员的封装,对Lua而言,我理解的封装应该是这样的:table类型变量内,某些成员能让外界访问,某些成员不能让外界访问。封装功能在Lua实现是比较麻烦的,《Programming in Lua》一书里面提到了几种可以实现私有访问的方法:

1使用闭包,像下面例子的代码newAccout返回的table,只能通过函数访问balance,外部不能直接访问balance。

function newAccount ()
    local self = {
            balance =0
        }
    local getBalance = function ()
         return self.balance
    end

    local setBalance = function (A)
          self.balance=A
    end

    return {getBalancegetBalance,setBalance=setBalance}
end

2Package,就是setfenv函数,改变当前的函数环境,来实现某些变量不可见,具体可以参考lua 5.1后增加的module函数。

3__index,使用元表,将私有值放入元表中,通过__index来控制。

         但是要做到像c++那样方便用public和private就能实现table成员的可见和不过见,基本不太可能。因为 Lua本身就是个脚本语言,讲究的是灵活性,封装成员已经偏离它的初衷了,所以,在封装这个问题上,我完全放弃考虑。

二、再来说多态,用过c++的就都知道用父类的指针调用一个虚函数,这个函数最后调的是父类的还是子类的,是不知道的,完全取决于这个指针在运行时指向了什么类型的数据。由于Lua的变量类型都是动态的,没有指针这个概念,所有的table变量都算是引用,通过 table调用它的一个成员函数,最后调用了它本身的,还是元表的,完全是在运行时决定,就这个角度来说,Lua天生就是具有多态的,不需要再实现。

三、而继承,这是Lua实现OOP的主要着力点,也是我们实现OOP的主要目的。哈哈,面向对象编程这种思想,在我来看,其实就是为了少写点代码,让子类可以复用父类的代码,本质就是一个懒字。

          所以,用Lua实现OOP最重要的就是能实现继承,继承,继承,重要的事情要说三次。

         下面开始简单说怎么用Lua实现继承,先说一下《Programming in Lua》中实现类的方法:

Account = {balance = 0}

function Account:new (o)
    o = o or {} -- create object if user does not provide one
    setmetatable(o, self)
    self.__index = self
    return o
end

function Account:deposit (v)
    self.balance = self.balance + v
end

function Account:withdraw (v)
    if v > self.balance then error"insufficient funds" end
    self.balance = self.balance - v
end

          上 述代码中new返回的table变量能够通过__index获得元表Accout的所有成员,Accout就相当于是类的原型,new函数返回值是类的实现。

《Programming in Lua》中实现类继承的方法,下述的SpecialAccount拥有了Account的deposit方法,并且重定义了withdraw方法:

SpecialAccount = Account:new()

function SpecialAccount:withdraw (v)
    if v - self.balance >= self:getLimit() then
        error"insufficient funds"
    end
    self.balance = self.balance - v
end

      接着我要说一下cocos2dx里面是怎么实现类的:

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

    --如果父类既不是函数也不是table则说明父类为空
    if superType ~= "function" and superType ~= "table" then
        superType = nil
        super = nil
    end

    --如果父类的类型是函数或者是C对象
    if superType == "function" or (super and super.__ctype == 1) then
        -- inherited from native C++ Object
        cls = {}

        --如果父类是表则复制成员并且设置这个类的继承信息
        --如果是函数类型则设置构造方法并且设置ctor函数
        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

        --定义该类型的创建实例的函数为基类的构造函数后复制到子类实例
        --并且调用子数的ctor方法
        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
        --如果是继承自普通的lua表,则设置一下原型,并且构造实例后也会调用ctor方法
        -- 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

          上面是cocos class的实现代码,它和《Programming in Lua》继承实现最大的不同点是,class提供了两种new的实现。其中一种实现就是支持对userdata的扩展,new出来的东西可以是一个userdata,但是将class的函数复制到了userdata上面,使得userdata也拥有了class的成员函数。

         上述lua 的class实现是一种比较成熟的方案,可以实现简单的lua对象继承,也可以实现对cocos对象的扩展,还支持多层继承,但是它也有一个明确的缺点,没有实现多基类继承。在下一篇文章中,我将会说明怎么实现多基类继承。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值