天天看點

Lua面向對象設計

  首先對于Lua語言,它沒有打算被用來進行大型的程式設計,相反,Lua目标定于小型到中型的程式設計,通常是作為大型系統的一部分,是以它隻提供了一套精簡的元素,很多進階語言的概念都沒有。這樣Lua就成為了一個既簡單又靈活的輕量級語言,但是基本上進階語言中的大多數機制都可以在現有Lua的基礎上加以實作。

  面向對象的基礎是類,但Lua中沒有提供類的概念,是以我們需要利用Lua現有的機制來實作類似于類的有關oop的一整套概念。基本方案是使用table來實作類機制,并且結合使用self參數和冒号操作。我們先來看看self參數和冒号操作符的用法:

  self參數的使用是很多面向對象語言的要點,大多數OO語言将這種機制隐藏起來,這樣程式員不必聲明這個參數(雖然仍然可以在方法内使用這個參數)。Lua也提供了通過使用冒号操作符來隐藏這個參數的聲明(通過冒号來聲明函數)。先來看下這段代碼(這裡面還不涉及到類的定義,隻是純粹為了解釋self和冒号操作):

  也可以将函數寫成這樣(将obj換成self能更好的表達出該參數的語義):

  self本身是一個Lua的保留字,為了不必給每個函數聲明都加上self,Lua提供了冒号操作符:

  定義函數時将點改成冒号,函數體内部直接用self參數通路對象自己(self參數表示表自己,也就是對象本身)。同時函數調用也有兩種寫法,并且這兩種寫法是通用的:

  點操作需要顯示傳入調用者自身。實際上,冒号的效果相當于在函數定義和函數調用的時候,增加一個額外的隐藏參數。這種方式隻是提供了一種友善的文法,并沒有什麼新的内容。我們可以使用dot文法定義函數而用冒号文法調用函數,反之亦然,隻要我們正确的處理好額外的 參。

   Lua中的表其實是一個對象。我們定義一個表作為原型(相當于類定義),然後其他的對象(也是一個表)通過metatable和__index機制來通路原型表中的成員。下面看下類的實作:

  為了簡潔,直接将類本身定為對象表的metatable。上面的new函數還是有一定的技巧性的,之前用過C++/Java等語言的人,應該不太習慣這種寫法。因為在強類型語言中,程式設計者會盡量避免傾向于展示技巧的寫法,而使用更簡單、可讀性更高的寫法。但在使用Lua時需要轉換一下思維,尤其是在基于Lua基礎的東西來擴充一些概念的時候,各種技巧性的東西會很常見,要習慣。

  類繼承的實作其實很簡單,直接調用基類的new操作,然後在子類中可以添加一些新的成員或方法:

  可以看到,其實上面的類也是對象,隻是看你怎麼去了解它,當把一個對象當做模闆原型來用時,它就是一個類,但同時,它也是一個對象,很随意有木有。

   多繼承:多繼承的想法很簡單,就是對于子類中不存在的域,在多個父類中依次查找即可。不過此代碼有點複雜,如下所示:

  函數search表示在父類清單list中依次查找名字為k的域。

  函數createClass用于建立多繼承的類,參數為父類清單。可以看到對象以多繼承類為metatable,而多繼承類通過metatable在多個父類中依次查找域。

  私有成員的實作:基于閉包來封裝掉不想被外部通路的資料或接口。

  一種特殊的“對象”:對象隻有一個單一的方法時,可以這樣使用。它是前面介紹的oop的一種特例,這種對象不具有繼承性,但具有私有性,而且效率比較高:

  從上面可以看出這種Single-Method對象實際上就是一個閉包。它不使用表,而是将一個哈桉樹看做一個對象。

  

繼續閱讀