在 Lua 中,我們可以使用表和函數(shù)實(shí)現(xiàn)面向?qū)ο?。將函?shù)和相關(guān)的數(shù)據(jù)放置于同一個(gè)表中就形成了一個(gè)對象。
請看文件名為 account.lua
的源碼:
local _M = {}
local mt = { __index = _M }
function _M.deposit (self, v)
self.balance = self.balance + v
end
function _M.withdraw (self, v)
if self.balance > v then
self.balance = self.balance - v
else
error("insufficient funds")
end
end
function _M.new (self, balance)
balance = balance or 0
return setmetatable({balance = balance}, mt)
end
return _M
引用代碼示例:
local account = require("account")
local a = account:new()
a:deposit(100)
local b = account:new()
b:deposit(50)
print(a.balance) --> output: 100
print(b.balance) --> output: 50
上面這段代碼 "setmetatable({balance = balance}, mt)",其中 mt 代表 { __index = _M }
,這句話值得注意。根據(jù)我們在元表這一章學(xué)到的知識(shí),我們明白,setmetatable 將 _M
作為新建表的原型,所以在自己的表內(nèi)找不到 'deposit'、'withdraw' 這些方法和變量的時(shí)候,便會(huì)到 __index 所指定的 _M 類型中去尋找。
繼承可以用元表實(shí)現(xiàn),它提供了在父類中查找存在的方法和變量的機(jī)制。在 Lua 中是不推薦使用繼承方式完成構(gòu)造的,這樣做引入的問題可能比解決的問題要多,下面一個(gè)是字符串操作類庫,給大家演示一下。
---------- s_base.lua
local _M = {}
local mt = { __index = _M }
function _M.upper (s)
return string.upper(s)
end
return _M
---------- s_more.lua
local s_base = require("s_base")
local _M = {}
_M = setmetatable(_M, { __index = s_base })
function _M.lower (s)
return string.lower(s)
end
return _M
---------- test.lua
local s_more = require("s_more")
print(s_more.upper("Hello")) -- output: HELLO
print(s_more.lower("Hello")) -- output: hello
在動(dòng)態(tài)語言中引入成員私有性并沒有太大的必要,反而會(huì)顯著增加運(yùn)行時(shí)的開銷,畢竟這種檢查無法像許多靜態(tài)語言那樣在編譯期完成。下面的技巧把對象作為各方法的 upvalue,本身是很巧妙的,但會(huì)讓子類繼承變得困難,同時(shí)構(gòu)造函數(shù)動(dòng)態(tài)創(chuàng)建了函數(shù),會(huì)導(dǎo)致構(gòu)造函數(shù)無法被 JIT 編譯。
在 Lua 中,成員的私有性,使用類似于函數(shù)閉包的形式來實(shí)現(xiàn)。在我們之前的銀行賬戶的例子中,我們使用一個(gè)工廠方法來創(chuàng)建新的賬戶實(shí)例,通過工廠方法對外提供的閉包來暴露對外接口。而不想暴露在外的例如 balance 成員變量,則被很好的隱藏起來。
function newAccount (initialBalance)
local self = {balance = initialBalance}
local withdraw = function (v)
self.balance = self.balance - v
end
local deposit = function (v)
self.balance = self.balance + v
end
local getBalance = function () return self.balance end
return {
withdraw = withdraw,
deposit = deposit,
getBalance = getBalance
}
end
a = newAccount(100)
a.deposit(100)
print(a.getBalance()) --> 200
print(a.balance) --> nil