Lua的多重继承实现

之前讲过在Lua中实现面向对象,今天详细聊下多重继承

继承

可以重新定义从基类继承的任意方法。只需要编写一个新方法就行了。
Lua语言中的对象有一个有趣的特性,就是无须为了指定一种新行为而创建一个新类。

多重继承

1.多重继承之在多个类中查找一个字段

其实多重继承没什么特别的,除非两个将要被继承的类有相同的函数名和属性,否则,处理起来很简单。

无非就是在多个table中查找某个字段而已,不简单吗?Lua里的继承就是在别人的table里查找自己不存在的字段罢了。

那么,单继承与多重继承的差别也在这里,一个是只查找一个table,另一个是查找两个或以上的table。

我们就先来看看如何从2个或多个table中查找某个字段,如下代码:

1
2
3
4
5
6
7
8
9
10
11
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、t1可以看成是两个类。

输出结果如下:

1
[LUA-print] who

2.多重继承之创建继承多个类的子类

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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函数就是用来创建一个继承了多个类的子类,有点小复杂,慢慢分析:

1) 参数是一个可变参数,我们要将多个被继承的类作为参数传递进来

2) parents用于保存这些被继承的类

3) 创建一个新的table——child,它就是我们想要的那个继承了多个类的子类

4) 给child设置元表,并且设置index元方法,index元方法可以是一个函数,当它是一个函数时,它的参数就是元表所属的table,以及要查找的字段名。

5) 我们在__index元方法函数里调用search函数,从多个父类中查找所需的字段。于是,当调用child的某个函数时,就会从各个父类中查找,这已经完成了继承的工作了。

6) 接下来就是我们所熟悉的new函数,用来创建child的子类。

7) 最后返回child,一切都完成了。

看似很复杂,其实还是对__index的应用而已。

我们赶紧来测试一下吧,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
--一个精灵类
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函数,创建一个继承了TSprite和TBullet的子类。

最后创建子类的对象,调用对象的hello和fire函数。

输出结果如下:

1
2
[LUA-print] 谁跟你hello!
[LUA-print] 别动,再动我就瞄不准了!

总结

  • 方法1,让__index字段成为一个函数,在该函数中搜索多个基类的方法段,搜索具有一定复杂性,性能不如单继承

  • 方法2,将继承的方法复制到子类中,缺点是当系统运行后就较难修改方法的定义。

  • 推荐第二种实现方式,在项目中已经使用了第2种方式

文章目录
  1. 1. 继承
  2. 2. 多重继承
    1. 2.1. 1.多重继承之在多个类中查找一个字段
    2. 2.2. 2.多重继承之创建继承多个类的子类
  3. 3. 总结
|