面向對象編程
對象的實現
在lua中table就是一種對象
1.有本身的狀態
2.有本身的惟一標識self
3.有本身的生命週期
使用table能夠本身實現面向對象的幾乎全部特性
把函數定義在table中,並使用t.func的形式訪問,如同方法調用
Account = {balance=0}
function Account.withdraw(v)
Account.balance = Account.ballance - v
end
但在函數中使用全局的Account是一個很差的習慣
在lua中使用面向對象方式編程時儘可能使用self和t:func的形式
帶有標識自身對象的方法定義:
function Account.withdraw(self, v)
同上的語法糖定義:
function Account:withdraw(v)
帶有標識自身對象的方法調用:
a1.withdraw(a1, v)
同上的語法糖定義:
a1:withdraw(v)
使用":"會把自身當作每個參數隱式的傳入
使用self是面向對象編程的一大核心,不少語言爲程序員隱藏了這個參數
self在C++中就至關於this指針
類的實現
在lua中沒有類的概念,但能夠本身來實現
function Account:new(o)
o = o or {} --若是用戶沒有提供table,則建立一個
setmetatable(o, self)
self.__index = self
return o
end
當使用new函數來建立對象後(其實就是建立一個新的table),全部的訪問都會從Account這個table裏找
這種狀況就至關於Account是一個類,也能夠說是一個原型模具,全部新建立的table都擁有他的屬性和方法
a = Account:new{balance=0}
a:deposit(100.00)
因爲deposit在新建立的table a裏沒有定義
所以經過它的元表__index來查找,a的元表是Account,
所以會調用Account的deposit方法,但self傳入的是a
這就實現了a繼承了Account的方法deposit
在這裏也看到了使用self來標識調用對象的好處
繼承和派生
sa = Account:new()
s = sa:new{limit=1000.00}
第一行sa繼承了Account,sa的元表是Account,找不到的方法就去Account裏去找
第二行s繼承了sa,這裏的new是Account的方法但傳入的self是sa,
導致s的元表是sa而sa的元表又是Account
因此一層一層的繼承了下去,而且在每一層的派生table裏均可以定義重載方法和新的方法
在lua裏的能夠實現多重繼承,就是使元表的__index指向一個函數,而後自行判斷並處理
私密性
使用table來實現面向對象的編程方式,幾乎能夠實現全部面向對象的編程特性
但它沒有也不想去實現的就是對象的私密性,也就是c++裏的private、public、protected
這與lua設計的初衷有關,lua定位於小型的程序開發,參與一個工程的人不會不少,自行約束
非要實現私密性的話lua也不是不能,只是不能再使用table和元表的方式了
可使用函數閉包來實現私密性:
function newAccount(init)
local self = {blance=init}
local withdraw = function(v)
self.balance = self.balance - v
end
local deposit = function(v)
self.balance = self.balance + v
end
return{withdraw = withdraw, deposit = deposit}
end
在閉包裏定義一個table的upvalue,而後把全部閉包函數都定義在這裏table裏,
而後返回這個table,用key訪問內部方法
使用閉包實現對象的方式比用table效率高並實現了絕對的私密性,但沒法實現繼承,至關於簡單的小對象
甚至能夠在閉包裏僅定義一個方法,而後經過key來判斷調用是什麼方法
Tcl/Tk對它的窗口部件就使用這種方法