一、元表、元方法
元表:metatable
元方法:metamethod
自定义table的行为,如table加法操作。
元表metatable
t, t1, t2 = {}, {}, {}
print(getmetatable(t)) --> nil,获取元表
setmetatable(t, t2) --> t设置元表为t2,任何表都可以是其他表的元表
setmetatable(t1, t2) --> t、t1共享元表t2,类似父类,描述共同行为
setmetatable(t2, t2) --> 元表是自己,描述私有行为
元方法(关系运算符)
local mt = {
-- 隐藏Set的元表,无法被外部getmetatable或者setmetatable
-- getmetatable时,返回__metatable值
-- setmetatable时,报错
__metatable = "not your business"
----------------关系运算符的元方法----------------
-- 只有下面三种:小于和等于的组合排列。为什么?见下面解释。
__lt = function(a, b) end -- a < b 时触发,类型不同会报错
__eq = function(a, b) end -- a == b时触发,必须有相同的元表,否则不会触发
__le = function(a, b) end -- a <= b时触发,类型不同会报错
--其他关系运算可被等价转换为上面的运算
--a > b 等价于 b < a,有人说这不废话嘛,但实际触发的元方法就不一样了。
--a >= b 等价于 b <= a
--a ~= b 等价于 not(a == b)
--!!注意a>b不等价于not(a<=b),偏序的时候,a>b和a<=b都可能不成立,比如两个不相见交的集合。
--综上原因,三个足矣。
}
----------------关系运算符定义技巧----------------
-- a < b 转换为:a <= b and not(b <= a)
-- a == b 转换为:a <=b and b <=a
-- 这样唯一需要定义的就是__le元方法。
mt.__le = function (a,b) -- set containment
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
mt.__lt = function (a,b)
return a <= b and not (b <= a)
end
mt.__eq = function (a,b)
return a <= b and b <= a
end
元方法(算术运算符)
local mt = {
----------------算术运算符的元方法----------------
-- Lua选择metamethod的过程:
-- a + b时,先看a有没有,再看b有没有,如果都没有就报错。
__add = function(a, b) end -- a + b时触发
__mul = function(a, b) end -- a * b时触发
__sub = function(a, b) end -- a - b时触发
__div = function(a, b) end -- a / b时触发
__unm = function(a) end -- -a 时触发
__pow = function(a, b) end -- a^b 时触发
__concat = function(a, b) end -- a..b 时触发
}
元方法(lua库定义)
local mt = {
----------------lua库定义的元方法----------------
__tostring = function(a) end -- print(a)时触发,print触发a.tostring
__index = function(a, b) end -- a.b,b不是a的域时触发,实现继承
__index = parent -- 同上,a.b等价于parent.b,这里也可能再触发__index
--rawset(a,b) 等价于a[b],不触发__index
__newindex = function(a, b) end --a.b = 1,b不是a的域时触发
__newindex = parent --同上,a.b=1 等价于 parent.b=1
--rawset(t,k,v)等价于t[k]=v,不触发__newindex
}
有默认值的表
local key = {} -- lua技巧,空表是唯一key的绝佳选择,任意创建的两个空表都不相等。
local mt = {__index = function (t) return t[key] end}
function setDefault (t, d)
t[key] = d
setmetatable(t, mt)
end
监控表
为表添加一个中间代理(空表)即可。
-------------监控表使用-------------
local test = {}
test = track(test) --对test域的访问和赋值都将被监控
-------------监控表实现-------------
-- 为表设置代理proxy,对proxy的访问就是对表的访问。
-- proxy一直为空,即可监控所有访问和赋值
-- 触发的元方法,又实际操作的是表的值
local index = {} -- 唯一key
local mt = {
__index = function (t,k) --监控域访问
return t[index][k]
end
__newindex = function (t,k,v) --监控域赋值
t[index][k] = v
end
}
function track (t) --为t返回一个代理:空表。
local proxy = {}
proxy[index] = t
setmetatable(proxy, mt)
return proxy
end
只读表
同监控表原理。
-------------只读表使用-------------
days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}
print(days[1]) --> Sunday
days[2] = "Noday" --> 报错:stdin:1: attempt to update a read-only table
-------------只读表实现-------------
function readOnly (t)
local proxy = {} --中间代理
local mt = {
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", 2)
end
}
setmetatable(proxy, mt)
return proxy
end
二、面向对象设计
Account = { a = "100" }
function Account.fuck(self, b)
print(b)
end
function Account:go()
print(self.a)
end
Account.fuck(100) --> 100
Account.go() --> 100
继承
元方法__index = table实现。这样的话只能指定一个table(父类)
Account = {balance = 0}
function Account:new (o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
function Account:deposit (v)
self.balance = self.balance + v
end
function Account:withdraw (v)
if v > self.balance then error"insufficient funds" end
self.balance = self.balance - v
end
多重继承
元方法__index = function,通过函数可以返回不同的table父类,达到多父类继承。
-- look up for `k' in list of tables 'plist'
local function search (k, plist)
for i=1, table.getn(plist) do
local v = plist[i][k] -- try 'i'-th superclass
if v then return v end
end
end
function createClass (...)
local c = {} -- new class
-- class will search for each method in the list of its
-- parents (`arg' is the list of parents)
setmetatable(c, {__index = function (t, k)
return search(k, arg)
end})
-- prepare `c' to be the metatable of its instances
c.__index = c
-- define a new constructor for this new class
function c:new (o)
o = o or {}
setmetatable(o, c)
return o
end
-- return new class
return c
end
成员私有
把对象拆分成:数据 + 操作。利用闭包,只需提供操作接口,即可调用数据。
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
单例
function newObject (value)
return function (action, v)
if action == "get" then return value
elseif action == "set" then value = v
else error("invalid action")
end
end
end
使用起来很简单:
d = newObject(0)
print(d("get")) --> 0
d("set", 10)
print(d("get")) --> 10
实战代码
local LayerInGame = class("LayerInGame", function() return display.newLayer() end)
-- 或者
local LayerInGame = class("LayerInGame", cc.Layer)
-- classname类名,...父类
function class(classname, ...)
local cls = { __cname = classname }
-- 所有的父类
local supers = { ...}
-- 遍历所有的父类,将父类的特性附加给cls
for _, super in ipairs(supers) do
local superType = type(super)
-- 父类必须是nil或者table或者function
assert(superType == "nil" or superType == "table" or superType == "function",
string.format("class() - create class \"%s\" with invalid super class type \"%s\"",
classname, superType))
-- 父类如果是函数
if superType == "function" then
-- __create创建函数不能重复
assert(cls.__create == nil,
string.format("class() - create class \"%s\" with more than one creating function",
classname));
-- if super is function, set it to __create
-- 直接将函数赋值给创建函数
cls.__create = super
elseif superType == "table" then
if super[".isclass"] then
-- 父类是一个cocos2dx的类,node,layer,sprite
assert(cls.__create == nil,
string.format("class() - create class \"%s\" with more than one creating function or native class",
classname));
-- 将父类的构建函数赋值给自己
cls.__create = function() return super:create() end
else
-- 一个普通的Lua table
-- 将所有的Lua table型父类保存在__supers中
cls.__supers = cls.__supers or { }
cls.__supers[#cls.__supers + 1] = super
-- 只将第一个父类赋值给super
if not cls.super then
-- set first super pure lua class as class.super
cls.super = super
end
end
else
error(string.format("class() - create class \"%s\" with invalid super type",
classname), 0)
end
end
-- 上面是解决了创建的问题,下面解决访问的问题
-- __index使得cls具备父类的属性
cls.__index = cls
if not cls.__supers or #cls.__supers == 1 then
-- 父类是普通的Luatable
setmetatable(cls, { __index = cls.super })
else
-- 遍历super一次访问父类的域,找到为止
setmetatable(cls, {
__index = function(_, key)
local supers = cls.__supers
for i = 1, #supers do
local super = supers[i]
if super[key] then return super[key] end
end
end
} )
end
-- 默认构造函数
if not cls.ctor then
cls.ctor = function() end
end
-- 创建cls,并调用默认构造函数
cls.new = function(...)
local instance
if cls.__create then
instance = cls.__create(...)
else
instance = { }
end
setmetatableindex(instance, cls)
instance.class = cls
instance:ctor(...)
return instance
end
-- 另一种构造函数
cls.create = function(_, ...)
return cls.new(...)
end
return cls
end