[译]简单声明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
相关文章
相关标签/搜索