[譯]簡單聲明Lua類

本文翻譯自 http://lua-users.org/wiki/SimpleLuaClasses ,轉載請註明出處。

聲明 Lua 類

Lua沒有class系統,可是它強有力的元程序設計使定義一個類變的容易。實際上,有不少方式去實現類定義,只是剛開始的時候可能會由於Lua陌生的符號而對Lua的面向對象感到困惑。函數

這裏描述的方法是最普通和靈活的,那就是用元表。table的行爲能夠經過給它設置一個元表並定義一些元方法來自定義。例如,一個元表有__index函數,那麼任何一次對錶中元素失敗的查詢都會傳遞給__index函數。若是__index是一張表的話,那麼會以一樣的方式對這張表進行查詢。(具體請參考《Lua程序設計第二版》第13章 元表與元方法)ui

這裏是基本的用法:google

 1 Account = {}
 2 Account.__index = Account
 3 
 4 function Account.create(balance)
 5    local acnt = {}             -- our new object
 6    setmetatable(acnt,Account)  -- make Account handle lookup
 7    acnt.balance = balance      -- initialize our object
 8    return acnt
 9 end
10 
11 function Account:withdraw(amount)
12    self.balance = self.balance - amount
13 end
14 
15 -- create and use an Account
16 acc = Account.create(1000)
17 acc:withdraw(100)

在這裏,acc就是一個只包含 balance 這個元素的表。Lua試圖去訪問acc中的 withdraw 方法,是訪問不到的,可是 acc 有元表而且定義了 __index 方法,那麼將會去這個元表(Account)裏去訪問 withdraw 方法。因此 acc:withdraw(100) 其實是調用的 Account.withdraw(acc, 100)。(關於 . 和 : 的區別請自行google)。咱們也能將 withdraw 直接放到 acc 中去,可是那樣太浪費,也不靈活,好比添加一個方法須要去改變 create 方法。lua

建立 Lua 類

瞭解了上面類的定義後,下面,我將定義一個 class() 函數去完成全部這些(甚至更多)轉換。spa

 1 Account = class(function(acc,balance)
 2               acc.balance = balance
 3            end)
 4 
 5 function Account:withdraw(amount)
 6    self.balance = self.balance - amount
 7 end
 8 
 9 -- can create an Account using call notation!
10 acc = Account(1000)
11 acc:withdraw(100)

在這段代碼中,提供了一個初始化函數去建立類,一個」構造函數「自動生成了。翻譯

同時支持簡單的繼承,例如,這裏基類 Animal 已經定義了,而且聲明瞭一些不一樣的子類。全部用 class() 函數生成的類都有一個 is_a() 的方法,去惟一肯定一個子類。設計

 1 -- animal.lua
 2 
 3 require 'class'
 4 
 5 Animal = class(function(a,name)
 6    a.name = name
 7 end)
 8 
 9 function Animal:__tostring()
10   return self.name..': '..self:speak()
11 end
12 
13 Dog = class(Animal)
14 
15 function Dog:speak()
16   return 'bark'
17 end
18 
19 Cat = class(Animal, function(c,name,breed)
20          Animal.init(c,name)  -- must init base!
21          c.breed = breed
22       end)
23 
24 function Cat:speak()
25   return 'meow'
26 end
27 
28 Lion = class(Cat)
29 
30 function Lion:speak()
31   return 'roar'
32 end
33 
34 fido = Dog('Fido')
35 felix = Cat('Felix','Tabby')
36 leo = Lion('Leo','African')
37 
38 
39 
40 D:\Downloads\func>lua -i animal.lua
41 > = fido,felix,leo
42 Fido: bark      Felix: meow     Leo: roar
43 > = leo:is_a(Animal)
44 true
45 > = leo:is_a(Dog)
46 false
47 > = leo:is_a(Cat)
48 true


全部的 Animal 子類都定義了 __tostring 這個元方法,任什麼時候候 Lua 在操做 Animal 子類的時候有用到 tostring() 這個函數時都會訪問到這個元方法。另外一方面,它依賴於 speak 函數,這是在子類中沒有定義的。全部者就是 C++ 使用者說的抽象基類。具體的派生類,好比 Dog ,本身定義 speak 。須要注意的是,若是派生類有本身的初始化函數,他們必須顯式的調用基類的初始化函數。code

class()實現

class() 有兩個技巧。因爲給這個類的元表設置了 __call 方法,它容許你直接調用 class() 去建立一個類,這個主要是用來複制基類中的變量元素。這不是惟一的方式去實現繼承,咱們也能經過設置 __index 來讓子類在訪問函數方法的時候來到基類的方法中,這種方式要好得多,精簡了類對象的數據。每一個派生類要去持有一個 _base 數據,用來實現 is_a 方法。對象

能夠看到,在運行時修改了基類並不會影響到它的子類。blog

 1 -- class.lua
 2 -- Compatible with Lua 5.1 (not 5.0).
 3 function class(base, init)
 4    local c = {}    -- a new class instance
 5    if not init and type(base) == 'function' then
 6       init = base
 7       base = nil
 8    elseif type(base) == 'table' then
 9     -- our new class is a shallow copy of the base class!
10       for i,v in pairs(base) do
11          c[i] = v
12       end
13       c._base = base
14    end
15    -- the class will be the metatable for all its objects,
16    -- and they will look up their methods in it.
17    c.__index = c
18 
19    -- expose a constructor which can be called by <classname>(<args>)
20    local mt = {}
21    mt.__call = function(class_tbl, ...)
22    local obj = {}
23    setmetatable(obj,c)
24    if init then
25       init(obj,...)
26    else 
27       -- make sure that any stuff from the base class is initialized!
28       if base and base.init then
29       base.init(obj, ...)
30       end
31    end
32    return obj
33    end
34    c.init = init
35    c.is_a = function(self, klass)
36       local m = getmetatable(self)
37       while m do 
38          if m == klass then return true end
39          m = m._base
40       end
41       return false
42    end
43    setmetatable(c, mt)
44    return c
45 end
相關文章
相關標籤/搜索