百度搜索一下,給出出的解決方案和學習帖子很多,可是我還是有很多的問題!
(1)什么是面向對象?
(2)lua中怎么實現面向對象?
(3)什么樣的實現既簡單又能完成我的功能?
(4)一定要按照c++的方式實現類的繼承嗎?
能力有限,在學習lua面向對象的時候我比較喜歡兩種實現方式:
1. 簡單粗暴的實現
參考[1]:參考同事的方法,如果借鑒、轉載請注明
在lua中,我們知道table是萬能的一個類型,當我們用table實現一個基類和子類的時候,他們都是確確實實存在的一個個table,都是有地址的。不會說像c++一樣,類定義是類定義,只有new出來才是真正可以使用的,而lua卻不是這樣。所以,最簡單的一種方法就是,每次繼承基類的時候,直接copy基類的所有內容,函數除外,其他的成員都重新復制一遍,如果是table的話重新構建一個,並且把內容拷貝過來。代碼如下:
--lua面向對象:簡單粗暴的實現 function copytable(tbArg) local tbCollsion = {} --local的作用域問題 local copy copy = function (obj) if type(obj) ~= "table" then return obj; end --檢查是否有嵌套循環的table if tbCollsion[obj] then return tbCollsion[obj]; end local tbNew = {} tbCollsion[obj] = tbNew; --遞歸復制 for k, v in pairs(obj) do tbNew[k] = copy(v); end --復制完之后,元表關系也要保留下來 return setmetatable(tbNew, getmetatable(obj)) end return copy(tbArg); end function inherit(tbBase, tbClass) --復制基類 local tbNew = copytable(tbBase) local tm = {} tm.__index = tbClass; setmetatable(tbNew, tm) --修改tbBase為tbClass中的成員或者新增加tbClass中的成員 if tbClass then for k, v in pairs(tbClass) do tbNew[k] = v end end return tbNew; end --使用 local tbObject = {} local tbFruit = inherit(tbObject) local tbApple = inherit(tbFruit) local tbBanana = inherit(tbFruit) local tbBanana1 = inherit(tbFruit) local tbBanana2 = inherit(tbFruit, tbBanana1)
優點:
(1)好理解
(2)好用,不會出現共用一個table引用,導致其中一個修改影響了另外一個
(3)共享函數引用
缺點:
(1)真正意義上的面向對象,繼承等概念不清晰
(2)一個子類new多個對象的概念是沒有的,和繼承已經混在了一起,所以如果要實現new多個對象的話,有代碼冗余
2. 概念意義上的實現
參考[2]:雲風的blog,如果借鑒、轉載請注明
這種從概念上實現面向對象的方法做到以下幾點:
(1)有類定義和對象的概念,類定義通過new來創建對象,並且同時調用自己的構造函數
(2)子類可以訪問基類的成員函數
(3)類定義不能夠調用函數(除了new之外),只有對象才能調用函數
(4)構造函數調用有和c++一樣的層級關系,先調用父類的構造函數,再調用子類的構造函數
--lua面向對象:概念意義上的實現 local _class={} function class(super) local class_type={} --注意:因為重載了__newindex函數, 所以ctor不要定義為nil class_type.ctor=false class_type.super=super class_type.new=function(...) local obj={} --下面的塊只做了一件事:依次從父類到當前子類調用構造函數ctor do local create create = function(c,...) if c.super then create(c.super,...) end if c.ctor then c.ctor(obj,...) end end create(class_type,...) end setmetatable(obj,{ __index=_class[class_type] }) return obj end --新加成員:防止定義類調用函數 local vtbl={} _class[class_type]=vtbl setmetatable(class_type,{__newindex= function(t,k,v) vtbl[k]=v end }) --只有定義類修改了__newindex --vbtl只屬於定義類 --new出來的對象共享所有定義類的引用,但獨享自己新增加的成員變量 if super then setmetatable(vtbl,{__index= function(t,k) local ret=_class[super][k] vtbl[k]=ret return ret end }) end return class_type end
說明幾點:
(1)只有定義類修改了__newindex
(2)vbtl只屬於定義類
(3)new出來的對象共享所有定義類的引用,但獨享自己新增加的成員變量
有點:
(1)概念上更加清晰,熟悉c++面向對象的很容易了解這個繼承的關系
(2)寫法上感覺很牛逼,做到了定義不能調用函數這一點
(3)共享函數引用
缺點:
(1)概念上清晰的成本是要更多的時間去理解
(2)雖然做到了c++類定義和對象上的概念區別,但是還是有多東西沒有實現
(3)對象也可以定義自己的函數,這一點就直接打破了區分定義和對象的本源,但是價值還是有的
(4)所有new出來對象共享類定義的引用對象,包括不需要復用的函數和table。由此多個對象共享一個定義的table很是個問題!
* 針對(4),可以通過實現定義的init函數,在init函數給不同的對象初始化不同的數據,即使是table!
參考:
[1]同事
[2]http://blog.codingnow.com/cloud/LuaOO