一、簡介
Lua是一門非常強大、非常靈活的腳本語言,自它從發明以來,無數的游戲使用了Lua作為開發語言。但是作為一款腳本語言,Lua也有着自己的不足,那就是它本身並沒有提供面向對象的特性,而游戲開發是一項龐大復雜的工程,如果沒有面向對象功能勢必會為開發帶來一定的不便。不過幸好Lua中有table這樣強大的數據結構,利用它再結合元表(metatable),我們便可以很方便地在Lua中模擬出類、繼承和多態等面向對象編程具有的特性。
二、前提知識
按照慣例,我們還是先來熟悉一下必要的前提知識,以便方便我們理解后續的代碼。
1.表(table)
(1)table 是 Lua 的一種數據結構,用於幫助我們創建不同的數據類型,如:數組、字典等;
(2)table 是一個關聯型數組,你可以用任意類型的值來作數組的索引,但這個值不能是 nil,所有索引值都需要用 "["和"]" 括起來;如果是字符串,還可以去掉引號和中括號; 即如果沒有[]括起,則認為是字符串索引,Lua table 是不固定大小的,你可以根據自己需要進行擴容;
(3)table 的默認初始索引一般以 1 開始,如果不寫索引,則索引就會被認為是數字,並按順序自動從1往后編;
(4)table 的變量只是一個地址引用,對 table 的操作不會產生數據影響;
(5)table 不會固定長度大小,有新數據插入時長度會自動增長;
(6)table 里保存數據可以是任何類型,包括function和table;
(7)table所有元素之間,總是用逗號 "," 隔開;
2.元表(metatable)
關於元表的概念以及它的要點,我們已經在《【游戲開發】小白學Lua——從Lua查找表元素的過程看元表、元方法》這篇博客中做了深入地探討,在此就不再贅述了,忘記了或者不熟悉的小伙伴可以去看一下。
三、Lua中實現類、繼承、多態
1.利用Lua實現類
在面向對象的特性中,類一般都有類名,構造方法,成員方法,屬性等。下面我們就用Lua中的table和元表實現一下模擬類中的這些特性,Class.lua 代碼如下:
1 --類的聲明,這里聲明了類名還有屬性,並且給出了屬性的初始值 2 Class = {x=0,y=0} 3 --設置元表的索引,想模擬類的話,這步操作很關鍵 4 Class.__index = Class 5 --構造方法,構造方法的名字是隨便起的,習慣性命名為new() 6 function Class:new(x,y) 7 local self = {} --初始化self,如果沒有這句,那么類所建立的對象如果有一個改變,其他對象都會改變 8 setmetatable(self, Class) --將self的元表設定為Class 9 self.x = x --屬性值初始化 10 self.y = y 11 return self --返回自身 12 end 13 14 --這里定義類的其他方法 15 function Class:test() 16 print(self.x,self.y) 17 end 18 19 function Class:plus() 20 self.x = self.x + 1 21 self.y = self.y + 1 22 end
簡單解釋一下,在Lua中的類,其實都是table,因為table既可以存儲普通變量又可以存儲函數或者另一個table,利用這個特性,我們實現了面向對象的類中的方法、屬性(字段)和構造方法。而設置元表和__index元方法這一步也是必不可少的,我們需要借助它的查找機制來實現類的繼承和多態等。
2.利用Lua實現繼承
在上面我們實現了Lua中的類,那么實現繼承也就不是什么難事了,SubClass.lua 代碼如下:
1 require 'Class' 2 3 --聲明了新的屬性Z 4 SubClass = {z = 0} 5 --設置元表為Class 6 setmetatable(SubClass, Class) 7 --還是和類定義一樣,表索引設定為自身 8 SubClass.__index = SubClass 9 --這里是構造方法 10 function SubClass:new(x,y,z) 11 local self = {} --初始化對象自身 12 self = Class:new(x,y) --將對象自身設定為父類,這個語句相當於其他語言的super ,可以理解為調用父類的構造函數 13 setmetatable(self, SubClass) --將對象自身元表設定為SubClass類 14 self.z= z --新的屬性初始化,如果沒有將會按照聲明=0 15 return self 16 end 17 18 --定義一個新的方法 19 function SubClass:go() 20 self.x = self.x + 10 21 end 22 23 --重定義父類的方法,相當於override 24 function SubClass:test() 25 print(self.x,self.y,self.z) 26 end
代碼里面的注釋已經很全了,關鍵點是通過設置SubClass的元表為它的父類Class,從而很方便地實現了繼承,這還是要歸功於table的查找機制。在子類SubClass中,我們可以自由地新增字段和子類獨有的新方法。而且還可以重定義或者說覆蓋/重寫父類的方法,類似於Java中的override,子類覆蓋父類的虛方法。有了這些我們就可以模擬面向對象中的多態了。
3.利用Lua實現多態
這里我們新建一個 Main.lua 將它作為我們程序的入口,在里面測試一下我們上面的代碼是否如我們所期待的那樣,Main.lua 代碼如下:
1 require 'Class' 2 require 'SubClass' 3 4 local a = Class:new() -- 首先實例化父類的對象,並調用父類中的方法 5 a:plus() 6 a:test() 7 8 a = SubClass:new() -- 然后實例化子類對象 9 a:plus() -- 子類對象可以訪問到父類中的成員和方法 10 a:go() -- 子類對象調用子類中的新增方法 11 a:test() -- 子類對象調用重寫的方法
程序運行的輸出結果如下:
1 1 11 1 0
首先我們實例化父類對象並調用父類中的方法,結果輸出了1 1,符合預期。接着我們再實例化了子類的對象,然后成功地訪問到了父類中的成員變量和方法,並且還可以訪問子類中的新增方法,最后我們再執行了重寫過父類中虛函數的方法,結果輸出 11 1 0,也是正確的。
四、總結
通過簡單地幾步,我們就在Lua中成功地模擬了類、繼承和多態的特性,這可以給我們程序開發帶來了不少的方便。以Unity游戲開發舉例,tolua/ulua是Unity游戲開發熱更新方案中的一種,他們功能很強大,但是美中不足的一點就是它們沒有提供面向對象的特性,所以在開發的時候,很多直接就是全局函數、全局變量和過程式的開發流程,影響了開發的效率,更對之后的維護帶來諸多不便。因此我們就可以通過與本篇中類似的方法,改進tolua/ulua,讓它們也可以實現面向對象開發。當然本篇中的代碼只是作為拋磚引玉,它其實是十分簡陋的,想用在商業項目中還需要做很多的改良與完善。至於如何改進tolua/ulua,讓他們支持面向對象特性,我們將在以后的篇章中繼續探討。
本篇博客中的代碼已經同步到Github:https://github.com/XINCGer/Unity3DTraining/tree/master/SomeTest/Lua_Class 歡迎fork!
作者:馬三小伙兒
出處:http://www.cnblogs.com/msxh/p/8469340.html
請尊重別人的勞動成果,讓分享成為一種美德,歡迎轉載。另外,文章在表述和代碼方面如有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!