A simple OOP library for Lua. It has inheritance, metamethods (operators), class variables and weak mixin support.
https://github.com/kikito/middleclass
函數(shù)功能解析
1. _createClasss(aClass, super)
返回默認(rèn)的表
local dict = {}
dict.__index = dict
local aClass = { name = name, super = super, static = {},
__instanceDict = dict, __declaredMethods = {},
subclasses = setmetatable({}, {__mode='k'}) }
創(chuàng)建一個(gè)table,包含下列屬性:
- name 類名
- super 父類的table
- static 靜態(tài)表
- __instanceDict 存放類定義的屬性和函數(shù)(包含父類)
- __decaredMethod (當(dāng)前類聲明的方法,不包含父類)
- subclasses 子類表, 使用弱引用
if super then
setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or super.static[k] end })
else
setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end })
end
設(shè)置static表的元表為 __index = __instanceDict(或者super.static)
setmetatable(aClass, { __index = aClass.static, __tostring = _tostring,
__call = _call, __newindex = _declareInstanceMethod })
設(shè)置類的元表:__index 為 static, __tostring => 默認(rèn)tostring,
_call =>調(diào)用static.new(), __newindex => _declareInstanceMethod
2. _includeMixin(aClass, mixin)
將一張表合并到另一張表中
for name,method in pairs(mixin) do
if name ~= "included" and name ~= "static" then aClass[name] = method end
end
- 除了"included"和"static",將mixin中的域全部賦值到aClass中(覆蓋)
for name,method in pairs(mixin.static or {}) do
aClass.static[name] = method
end
- 將mixin.static中的域,賦值到aClass.static中
if type(mixin.included)=="function" then mixin:included(aClass) end
- 如果mixin包含included方法,則調(diào)用mixin的included方法,參數(shù)為aClass
3. DefaultMixin
** 默認(rèn)合并表,創(chuàng)建類時(shí)自動(dòng)添加到類中**
提供如下方法:
- __tostring 默認(rèn)tostring方法
- initialize 初始化方法 類似于構(gòu)造函數(shù)
isInstanceOf = function(self, aClass)
return type(aClass) == 'table' and (aClass == self.class or self.class:isSubclassOf(aClass))
end,
- isInstanceOf 判斷是否是某個(gè)類的實(shí)例
為static添加方法:
allocate = function(self)
assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
return setmetatable({ class = self }, self.__instanceDict)
end,
-
allocate 創(chuàng)建一個(gè)實(shí)例表
- 添加class屬性,指向類表
- 將類表的__instanceDict設(shè)置為元表
new = function(self, ...)
assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'")
local instance = self:allocate()
instance:initialize(...)
return instance
end,
new 通過allocate創(chuàng)建表,并調(diào)用initialize方法
subclassed 創(chuàng)建子類后的回調(diào)
subclass = function(self, name)
assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
assert(type(name) == "string", "You must provide a name(string) for your class")
local subclass = _createClass(name, self)
for methodName, f in pairs(self.__instanceDict) do
_propagateInstanceMethod(subclass, methodName, f)
end
subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end
self.subclasses[subclass] = true
self:subclassed(subclass)
return subclass
end,
-
subclass 為類添加一個(gè)指定名字的子類,并返回子類
- 創(chuàng)建指定名字的類,父類為自己
- 將父類的__instanceDict中的所有域,復(fù)制到子類中
- 子類的initialize 默認(rèn)調(diào)用父類的initialize
- 在自己的subclass索引中,添加子類的索引
- 調(diào)用自己的subclassed方法,參數(shù)為子類
isSubclassOf = function(self, other)
return type(other) == 'table' and
type(self.super) == 'table' and
( self.super == other or self.super:isSubclassOf(other) )
end,
- isSubclassOf 是否是指定類的子類
include = function(self, ...)
assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
return self
end
- include 將參數(shù)合并到本類中
4. _declareInstanceMethod(aClass, name, f)
在類中添加聲明方法和屬性
local function _declareInstanceMethod(aClass, name, f)
aClass.__declaredMethods[name] = f
if f == nil and aClass.super then
f = aClass.super.__instanceDict[name]
end
_propagateInstanceMethod(aClass, name, f)
end
- 注冊(cè)到__declaredMethods
- 如果f為nil,則去父類取該字段
- 將域添加到子類中
5. _propagateInstanceMethod(aClass, name, f)
添加域到表中,并添加到所有的子類中,相當(dāng)于繼承
local function _propagateInstanceMethod(aClass, name, f)
f = name == "__index" and _createIndexWrapper(aClass, f) or f
aClass.__instanceDict[name] = f
for subclass in pairs(aClass.subclasses) do
if rawget(subclass.__declaredMethods, name) == nil then
_propagateInstanceMethod(subclass, name, f)
end
end
end
- 如果name = __index, 調(diào)用_createIndexWrapper
- 將f添加到aClass.__instanceDict[name]中
- 遍歷所有子類,如果子類不包含該方法,則添加到子類中(若包含,相當(dāng)于重寫)
6. _createIndexWrapper(aClass, f)
對(duì)__index處理
local function _createIndexWrapper(aClass, f)
if f == nil then
return aClass.__instanceDict
else
return function(self, name)
local value = aClass.__instanceDict[name]
if value ~= nil then
return value
elseif type(f) == "function" then
return (f(self, name))
else
return f[name]
end
end
end
end
- f為空,則返回aClass.__instanceDict
- 如果__instanceDict包含name 則返回 <==> __index = __instanceDict
- 如果f為函數(shù), __index = f(self,name)
- 否則,返回f[name]
7. _call(self, ...)
調(diào)用new方法,使A:new() <==> A()
8. _tostring(self)
** 默認(rèn)tostring方法**
9. middleclass.class(name, super)
創(chuàng)建一個(gè)類,并設(shè)置父類
- 如果super ~= nil 則調(diào)用subclass, 將那么添加到super的子類中
- 否則,創(chuàng)建一張默認(rèn)表,并將DefaultMixin合并進(jìn)去
實(shí)例解析
Metamethods
不需設(shè)置metatable的元方法,直接在當(dāng)前類中定義,即可實(shí)現(xiàn)原方法。
以tostring為例:
Point = class('Point')
function Point:initialize(x,y)
self.x = x
self.y = y
end
function Point:__tostring()
return 'Point: [' .. tostring(self.x) .. ', ' .. tostring(self.y) .. ']'
end
p1 = Point(100, 200)
p2 = Point(35, -10)
print(p1)
print(p2)
Output:
Point: [100, 200]
Point: [35, -10]
- p1(實(shí)例)的__index為__instanceDict,所以調(diào)用tostring時(shí),會(huì)去__instanceDict中去查找
- Pointer的__newindex為_declareInstanceMethod,在類中定義__tostring時(shí),_declareInstanceMethod會(huì)將該方法加入到__instanceDict中
- 重寫__tostring,相當(dāng)于在自己的元表中定義該方法
- 其他元方法類似
Middleclass 4.x supports all the Lua metamethods. There are some restrictions:
- Overriding the
metatablemetamethod is possible, but likely will give you trouble. Do this only if you know what you are doing. - Metamethods not supported by the Lua version being used are not supported. For example, the
lenmetamethod does not work in Lua 5.1 or LuaJIT without 5.2 compatibility.
mixins
類變量提供include方法,可以將一個(gè)table的域拷貝到另一個(gè)table中,被拷貝的table會(huì)觸發(fā)included方法
- class.static表中提供include方法
- class的__index指向class.static
- 被合并的table 調(diào)用include方法
Private stuff
在類內(nèi)定義局部變量,或者局部函數(shù),外部不可訪問
lua的作用于限制
其他
class = require("middleclass")
A = class("A")
A.n = 1
B = A()
print(B.n)
B.n = 2
print(B.n)
print(A.n)
Output:
1
2
1
B是A的實(shí)例,B繼承了n(B中沒有n屬性,則去__instanceDict中取)
當(dāng)B對(duì)n賦值,則相當(dāng)于在B表中添加屬性,不會(huì)改變__instanceDict的值,再次獲取該屬性時(shí),直接從B表中讀,所以值為2,且不會(huì)改變A中的值
middleclass使用的表的簡單總結(jié)
- __instanceDict 記錄當(dāng)前類,以及所有父類定義的實(shí)例方法(屬性), __index指向自己
- __declaredMethods 記錄當(dāng)前類聲明的方法(屬性)
- subclasses 當(dāng)前類的所有子類,弱引用
-
static 靜態(tài)表,定義new, include, isSubclassOf等方法,__index指向__instanceDict。
實(shí)例變量不能直接訪問static表,必須通過.class.static訪問。
類的靜態(tài)方法可以通過class.static:func 來定義 -
類的默認(rèn)表 定義__instanceDict,__declaredMethods, static等屬性。
__index指向static表,可以直接使用static中的字段(A:new())。
__newIndex為_declareInstanceMethod方法,添加字段時(shí)會(huì)更新__instanceDict以及__declareMethods - 實(shí)例表 定義class屬性,指向當(dāng)前的類。 metatable為對(duì)應(yīng)類的__instanceDict