学习Cocos2d-x Lua:面向对象 -- 多重继承、私密性
在这个系列中,我们主要聚焦于学习Cocos2d-x Lua,总结Lua开发过程中涉及的知识点,以及探讨如何在开发过程中使用Cocos Code IDE。本篇文章将深入讲解Lua面向对象编程中的多重继承和私密性,包括如何在多个类中查找一个字段、创建继承多个类的子类,以及实现类的私密性。
1. 多重继承之在多个类中查找一个字段
多重继承本身并不复杂,除非两个将要被继承的类存在相同的函数名和属性,否则处理起来相对简单。其实质就是在多个table中查找某个字段,因为在Lua里,继承的实现方式就是在其他table中查找自身不存在的字段。
单继承与多重继承的区别在于,单继承只查找一个table,而多重继承需要查找两个或以上的table。下面我们来看看如何从2个或多个table中查找某个字段,示例代码如下:
function search(classes, key)
for i = 1, #classes do
local value = classes[i][key];
if value ~= nil then
return value;
end
end
end
local t1 = {name = "hehe"};
local t2 = {game = "who"};
print(search({t1, t2}, "game"));
在上述代码中,classes 参数是一个table,其中存放了多个table,这些table代表了我们想要继承的类。key 则是要查找的字段。代码通过遍历所有的table,判断该字段是否存在于某个table中,若找到则返回该值。
测试代码是从 t1 和 t2 中查找 game 字段,这里的 t1 和 t2 可看作两个类。运行结果如下:
[LUA-print] who
2. 多重继承之创建继承多个类的子类
前面的 search 函数相对简单,它只是一个预热。真正创建多重继承的函数会复杂一些,示例代码如下:
function createClass(...)
local parents = {...};
local child = {};
-- 设置类的元表
setmetatable(child, {
__index = function(table, key)
return search(parents, key);
end
})
-- 给类新增一个new函数,用于创建对象
function child:new()
o = {};
setmetatable(o, child);
child.__index = child;
return o;
end
-- 返回这个继承了多个类的子类
return child;
end
createClass 函数用于创建一个继承了多个类的子类,下面对其进行详细分析:
- 参数:该函数接受可变参数,我们需要将多个被继承的类作为参数传递进来。
- 保存父类:
parents用于保存这些被继承的类。 - 创建子类:创建一个新的table
child,它就是我们想要的继承了多个类的子类。 - 设置元表和
__index元方法:给child设置元表,并设置__index元方法。当__index是一个函数时,其参数为元表所属的table和要查找的字段名。 - 实现继承:在
__index元方法函数里调用search函数,从多个父类中查找所需的字段。这样,当调用child的某个函数时,就会从各个父类中查找,从而完成继承的工作。 - 创建对象的
new函数:new函数用于创建child的子类,实现方式与之前文章中所述相同。若忘记相关内容,可查看这篇文章。 - 返回子类:最后返回
child,完成子类的创建。
虽然看起来复杂,但实际上还是对 __index 的应用。下面是测试代码:
-- 一个精灵类
TSprite = {}
function TSprite:hello()
print("谁跟你hello!");
end
function TSprite:new()
o = {}
setmetatable(o, self);
self.__index = self;
return o;
end
-- 一个子弹类
TBullet = {}
function TBullet:fire()
print("别动,再动我就瞄不准了!");
end
function TBullet:new()
o = {}
setmetatable(o, self);
self.__index = self;
return o;
end
-- 继承了两个类的子类
local BulletSprite = createClass(TSprite, TBullet);
-- 子类的对象
local bSprite = BulletSprite:new();
bSprite:hello();
bSprite:fire();
上述代码创建了两个类 TSprite 和 TBullet,然后调用 createClass 函数创建一个继承了这两个类的子类 BulletSprite,最后创建子类的对象并调用其 hello 和 fire 函数。运行结果如下:
[LUA-print] 谁跟你hello!
[LUA-print] 别动,再动我就瞄不准了!
3. 类的私密性
接下来介绍一个与多重继承无关的技巧——类的私密性。在Java、C++等语言中,我们熟悉 public、private、protected 等关键词,这些关键词使得封装成为可能。然而,在Lua里并没有私密的概念,类本质上是一个table,table的所有字段都可以被调用,不存在公有和私有之分。
如果希望某些函数和属性不被外部调用,也可以实现,但实现方式可能看起来有些别扭,示例代码如下:
function createTSprite()
local self = {name = "benmutou"};
local function myBus()
print("myBus是我自己的函数,你不能直接调用");
end
local function myGame()
print("myGame是我自己的函数,你不能直接调用");
end
local function hello()
print("hello:");
myBus();
end
local function hi()
print("hi:");
myGame();
end
local function setName(newName)
self.name = newName;
end
return {hello = hello, hi = hi, setName = setName};
end
在上述代码中,我们不再使用冒号来定义函数。类中的 name、myBus、myGame 不希望被外部直接调用。调用 createTSprite 函数后,会返回一个新的table,该table仅存放了能够被外部直接调用的函数或属性。
下面是测试代码:
local sp = createTSprite();
sp.hello();
sp.hi();
运行结果如下:
[LUA-print] hello:
[LUA-print] myBus是我自己的函数,你不能直接调用
[LUA-print] hi:
[LUA-print] myGame是我自己的函数,你不能直接调用
通过这种方式,我们创建的对象只能使用 hello、hi、setName 函数,而其他的 name、myBus、myGame 只能通过这些可使用的函数间接调用,从而实现了类的私密性。不过,这种实现方式可能会让人感觉不太像传统意义上的类。
4. 总结
关于面向对象的内容,本文暂时介绍到这里。文中内容可能较为基础,目的是为了巩固Lua基础。后续我们将继续深入探讨Cocos2d-x Lua的相关知识。