一、元表、元方法

元表:metatable
元方法:metamethod
自定义table的行为,如table加法操作。

元表metatable

  1. t, t1, t2 = {}, {}, {}
  2. print(getmetatable(t)) --> nil,获取元表
  3. setmetatable(t, t2) --> t设置元表为t2,任何表都可以是其他表的元表
  4. setmetatable(t1, t2) --> tt1共享元表t2,类似父类,描述共同行为
  5. setmetatable(t2, t2) --> 元表是自己,描述私有行为

元方法(关系运算符)

  1. local mt = {
  2. -- 隐藏Set的元表,无法被外部getmetatable或者setmetatable
  3. -- getmetatable时,返回__metatable
  4. -- setmetatable时,报错
  5. __metatable = "not your business"
  6. ----------------关系运算符的元方法----------------
  7. -- 只有下面三种:小于和等于的组合排列。为什么?见下面解释。
  8. __lt = function(a, b) end -- a < b 时触发,类型不同会报错
  9. __eq = function(a, b) end -- a == b时触发,必须有相同的元表,否则不会触发
  10. __le = function(a, b) end -- a <= b时触发,类型不同会报错
  11. --其他关系运算可被等价转换为上面的运算
  12. --a > b 等价于 b < a,有人说这不废话嘛,但实际触发的元方法就不一样了。
  13. --a >= b 等价于 b <= a
  14. --a ~= b 等价于 not(a == b)
  15. --!!注意a>b不等价于not(a<=b),偏序的时候,a>ba<=b都可能不成立,比如两个不相见交的集合。
  16. --综上原因,三个足矣。
  17. }
  18. ----------------关系运算符定义技巧----------------
  19. -- a < b 转换为:a <= b and not(b <= a)
  20. -- a == b 转换为:a <=b and b <=a
  21. -- 这样唯一需要定义的就是__le元方法。
  22. mt.__le = function (a,b) -- set containment
  23. for k in pairs(a) do
  24. if not b[k] then return false end
  25. end
  26. return true
  27. end
  28. mt.__lt = function (a,b)
  29. return a <= b and not (b <= a)
  30. end
  31. mt.__eq = function (a,b)
  32. return a <= b and b <= a
  33. end

元方法(算术运算符)

  1. local mt = {
  2. ----------------算术运算符的元方法----------------
  3. -- Lua选择metamethod的过程:
  4. -- a + b时,先看a有没有,再看b有没有,如果都没有就报错。
  5. __add = function(a, b) end -- a + b时触发
  6. __mul = function(a, b) end -- a * b时触发
  7. __sub = function(a, b) end -- a - b时触发
  8. __div = function(a, b) end -- a / b时触发
  9. __unm = function(a) end -- -a 时触发
  10. __pow = function(a, b) end -- a^b 时触发
  11. __concat = function(a, b) end -- a..b 时触发
  12. }

元方法(lua库定义)

  1. local mt = {
  2. ----------------lua库定义的元方法----------------
  3. __tostring = function(a) end -- print(a)时触发,print触发a.tostring
  4. __index = function(a, b) end -- a.bb不是a的域时触发,实现继承
  5. __index = parent -- 同上,a.b等价于parent.b,这里也可能再触发__index
  6. --rawset(a,b) 等价于a[b],不触发__index
  7. __newindex = function(a, b) end --a.b = 1b不是a的域时触发
  8. __newindex = parent --同上,a.b=1 等价于 parent.b=1
  9. --rawset(t,k,v)等价于t[k]=v,不触发__newindex
  10. }

有默认值的表

  1. local key = {} -- lua技巧,空表是唯一key的绝佳选择,任意创建的两个空表都不相等。
  2. local mt = {__index = function (t) return t[key] end}
  3. function setDefault (t, d)
  4. t[key] = d
  5. setmetatable(t, mt)
  6. end

监控表

为表添加一个中间代理(空表)即可。

  1. -------------监控表使用-------------
  2. local test = {}
  3. test = track(test) --对test域的访问和赋值都将被监控
  4. -------------监控表实现-------------
  5. -- 为表设置代理proxy,对proxy的访问就是对表的访问。
  6. -- proxy一直为空,即可监控所有访问和赋值
  7. -- 触发的元方法,又实际操作的是表的值
  8. local index = {} -- 唯一key
  9. local mt = {
  10. __index = function (t,k) --监控域访问
  11. return t[index][k]
  12. end
  13. __newindex = function (t,k,v) --监控域赋值
  14. t[index][k] = v
  15. end
  16. }
  17. function track (t) --为t返回一个代理:空表。
  18. local proxy = {}
  19. proxy[index] = t
  20. setmetatable(proxy, mt)
  21. return proxy
  22. end

只读表

同监控表原理。

  1. -------------只读表使用-------------
  2. days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}
  3. print(days[1]) --> Sunday
  4. days[2] = "Noday" --> 报错:stdin:1: attempt to update a read-only table
  5. -------------只读表实现-------------
  6. function readOnly (t)
  7. local proxy = {} --中间代理
  8. local mt = {
  9. __index = t,
  10. __newindex = function (t,k,v)
  11. error("attempt to update a read-only table", 2)
  12. end
  13. }
  14. setmetatable(proxy, mt)
  15. return proxy
  16. end

二、面向对象设计

  1. Account = { a = "100" }
  2. function Account.fuck(self, b)
  3. print(b)
  4. end
  5. function Account:go()
  6. print(self.a)
  7. end
  8. Account.fuck(100) --> 100
  9. Account.go() --> 100

继承

元方法__index = table实现。这样的话只能指定一个table(父类)

  1. Account = {balance = 0}
  2. function Account:new (o)
  3. o = o or {}
  4. setmetatable(o, self)
  5. self.__index = self
  6. return o
  7. end
  8. function Account:deposit (v)
  9. self.balance = self.balance + v
  10. end
  11. function Account:withdraw (v)
  12. if v > self.balance then error"insufficient funds" end
  13. self.balance = self.balance - v
  14. end

多重继承

元方法__index = function,通过函数可以返回不同的table父类,达到多父类继承。

  1. -- look up for `k' in list of tables 'plist'
  2. local function search (k, plist)
  3. for i=1, table.getn(plist) do
  4. local v = plist[i][k] -- try 'i'-th superclass
  5. if v then return v end
  6. end
  7. end
  8. function createClass (...)
  9. local c = {} -- new class
  10. -- class will search for each method in the list of its
  11. -- parents (`arg' is the list of parents)
  12. setmetatable(c, {__index = function (t, k)
  13. return search(k, arg)
  14. end})
  15. -- prepare `c' to be the metatable of its instances
  16. c.__index = c
  17. -- define a new constructor for this new class
  18. function c:new (o)
  19. o = o or {}
  20. setmetatable(o, c)
  21. return o
  22. end
  23. -- return new class
  24. return c
  25. end

成员私有

把对象拆分成:数据 + 操作。利用闭包,只需提供操作接口,即可调用数据。

  1. function newAccount (initialBalance)
  2. local self = {balance = initialBalance}
  3. local withdraw = function (v) --闭包,操作接口
  4. self.balance = self.balance - v
  5. end
  6. local deposit = function (v) --闭包,操作接口
  7. self.balance = self.balance + v
  8. end
  9. local getBalance = function () return self.balance end
  10. return {
  11. withdraw = withdraw,
  12. deposit = deposit,
  13. getBalance = getBalance
  14. }
  15. end

单例

  1. function newObject (value)
  2. return function (action, v)
  3. if action == "get" then return value
  4. elseif action == "set" then value = v
  5. else error("invalid action")
  6. end
  7. end
  8. end
  9. 使用起来很简单:
  10. d = newObject(0)
  11. print(d("get")) --> 0
  12. d("set", 10)
  13. print(d("get")) --> 10

实战代码

  1. local LayerInGame = class("LayerInGame", function() return display.newLayer() end)
  2. -- 或者
  3. local LayerInGame = class("LayerInGame", cc.Layer)
  4. -- classname类名,...父类
  5. function class(classname, ...)
  6. local cls = { __cname = classname }
  7. -- 所有的父类
  8. local supers = { ...}
  9. -- 遍历所有的父类,将父类的特性附加给cls
  10. for _, super in ipairs(supers) do
  11. local superType = type(super)
  12. -- 父类必须是nil或者table或者function
  13. assert(superType == "nil" or superType == "table" or superType == "function",
  14. string.format("class() - create class \"%s\" with invalid super class type \"%s\"",
  15. classname, superType))
  16. -- 父类如果是函数
  17. if superType == "function" then
  18. -- __create创建函数不能重复
  19. assert(cls.__create == nil,
  20. string.format("class() - create class \"%s\" with more than one creating function",
  21. classname));
  22. -- if super is function, set it to __create
  23. -- 直接将函数赋值给创建函数
  24. cls.__create = super
  25. elseif superType == "table" then
  26. if super[".isclass"] then
  27. -- 父类是一个cocos2dx的类,nodelayersprite
  28. assert(cls.__create == nil,
  29. string.format("class() - create class \"%s\" with more than one creating function or native class",
  30. classname));
  31. -- 将父类的构建函数赋值给自己
  32. cls.__create = function() return super:create() end
  33. else
  34. -- 一个普通的Lua table
  35. -- 将所有的Lua table型父类保存在__supers
  36. cls.__supers = cls.__supers or { }
  37. cls.__supers[#cls.__supers + 1] = super
  38. -- 只将第一个父类赋值给super
  39. if not cls.super then
  40. -- set first super pure lua class as class.super
  41. cls.super = super
  42. end
  43. end
  44. else
  45. error(string.format("class() - create class \"%s\" with invalid super type",
  46. classname), 0)
  47. end
  48. end
  49. -- 上面是解决了创建的问题,下面解决访问的问题
  50. -- __index使得cls具备父类的属性
  51. cls.__index = cls
  52. if not cls.__supers or #cls.__supers == 1 then
  53. -- 父类是普通的Luatable
  54. setmetatable(cls, { __index = cls.super })
  55. else
  56. -- 遍历super一次访问父类的域,找到为止
  57. setmetatable(cls, {
  58. __index = function(_, key)
  59. local supers = cls.__supers
  60. for i = 1, #supers do
  61. local super = supers[i]
  62. if super[key] then return super[key] end
  63. end
  64. end
  65. } )
  66. end
  67. -- 默认构造函数
  68. if not cls.ctor then
  69. cls.ctor = function() end
  70. end
  71. -- 创建cls,并调用默认构造函数
  72. cls.new = function(...)
  73. local instance
  74. if cls.__create then
  75. instance = cls.__create(...)
  76. else
  77. instance = { }
  78. end
  79. setmetatableindex(instance, cls)
  80. instance.class = cls
  81. instance:ctor(...)
  82. return instance
  83. end
  84. -- 另一种构造函数
  85. cls.create = function(_, ...)
  86. return cls.new(...)
  87. end
  88. return cls
  89. end