1. 程式人生 > >(轉載)【笨木頭Lua專欄】基礎補充21:面向對象——多重繼承、私密性

(轉載)【笨木頭Lua專欄】基礎補充21:面向對象——多重繼承、私密性

子類 先來 nta 參數 hive lua 封裝 完成 存在

在Lua中的多重繼承和私密性可能用得比較少,也可能只是我個人用得比較少。

本來想偷懶不寫這文章的,因為我今天剛買了個漂移板,連起步都還沒學會啊,想多學一會。

咳咳,本著堅持不懈、負責到底的態度,我還是決定隨便寫幾句~(小若:隨便寫幾句是幾噸意思啊?!)

笨木頭花心貢獻,哈?花心?不,是用心~

轉載請註明,原文地址:http://www.benmutou.com/archives/1800

文章來源:笨木頭與遊戲開發

1.多重繼承之在多個類中查找一個字段

我發現這些高(shen)智(jing)商(bing)人群真的很厲害,這種技巧都能想到,很佩服。

其實多重繼承沒什麽特別的,除非兩個將要被繼承的類有相同的函數名和屬性,否則,處理起來很簡單。

無非就是在多個table中查找某個字段而已,不簡單嗎?Lua裏的繼承就是在別人的table裏查找自己不存在的字段罷了。

那麽,單繼承與多重繼承的差別也在這裏,一個是只查找一個table,另一個是查找兩個或以上的table。

我們就先來看看如何從2個或多個table中查找某個字段,如下代碼:

1
2
3
4
5
6
7
8
9
10
11
12
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可以看成是兩個類。

輸出結果如下:

[LUA-print] who

2.多重繼承之創建繼承多個類的子類

剛剛的search函數很簡單吧?別急著開心,那只是預熱一下而已,真正創建多重繼承的函數比較復雜。

如下代碼:

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的子類,實現方式和上一篇所說的是一樣 ,如果你忘記了,可以看看這篇文章:http://www.benmutou.com/archives/1791

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函數。

輸出結果如下:

[LUA-print] 誰跟你hello!
[LUA-print] 別動,再動我就瞄不準了!

怎麽樣?很簡單吧~

3.類的私密性

這裏來說一個和多重繼承無關的技巧,那就是私密性。

對於Java、C++等語言,我們都很熟悉,public、private、protected等關鍵詞。

這些關鍵詞讓封裝成為了可能。

然後,Lua裏是沒有私密這種說法的,類也是一個table,table的所有字段都是可以調用的,並沒有說哪些是公有的,哪些是私有的。

如果有某些函數和屬性不希望被外部調用,那麽,也可以,不過這種實現方式看起來很別扭:

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
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僅僅存放了一些字段,這些字段就是能夠被外部直接調用的函數或者是屬性。

來看看測試代碼:

1
2
3
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基礎。

原文地址:http://www.benmutou.com/archives/1800

(轉載)【笨木頭Lua專欄】基礎補充21:面向對象——多重繼承、私密性