写在前面
我们知道lua本身是不支持继承的,在lua中所有的对象都是由table组成的,这里我们都知道可以使用元表来模拟类的继承。其中核心实现就是用的__index元方法。简单的实现如下:
Super={} --父类
SubClass={} --子类
setmetatable(SubClass,{__index=Super}) --模拟继承
简单封装
我们先来看看一个简单的封装继承的方式。
BaseObject = {}
BaseObject .__index = BaseObject
function BaseObject:New(object,...)
object = object or {}
setmetatable(object, self)
self.__index = self
if object.Ctor then
object:Ctor(...)
end
return object
end
这里实现了一个简单的基类,并实现了一个简单的构造方法,可以传入值。但是个人觉得使用这个构造方法不是很好,还不如直接写一个方法在外面写一个初始化方法。
middleclass封装
这是一个github上面有人给出的一个lua继承的方案,特点就是简单实用。github地址,用的版本是4.1。
简单用例:
local class = require 'middleclass'
Person = class('Person') --声明一个Person类
function Person:initialize(name)
self.name = name
end
function Person:speak()
print('Hi, I am ' .. self.name ..'.')
end
AgedPerson = class('AgedPerson', Person) -- 声明一个继承于Person的AgedPerson类
AgedPerson.static.ADULT_AGE = 18 --设置一个变量
function AgedPerson:initialize(name, age)
Person.initialize(self, name) -- 调用父类的构造方法
self.age = age
end
function AgedPerson:speak()
Person.speak(self) -- 打印 "Hi, I am xx."
if(self.age < AgedPerson.ADULT_AGE) then --调用子类的一个变量
print('I am underaged.')
else
print('I am an adult.')
end
end
local p1 = AgedPerson:new('Billy the Kid', 13) -- 实例化对象
local p2 = AgedPerson:new('Luke Skywalker', 21)
p1:speak()
p2:speak()
输出:
Hi, I'm Billy the Kid.
I am underaged.
Hi, I'm Luke Skywalker.
I am an adult.
这个是官方给出的一个例子。非常简单容易的去理解。还有一些官方的教程在这里
解析:
我们从 class('Person')这一句开始解析嘛,正好可以看一下他的一个工作流程。
这里是调用的__call元方法,元方法中调用的middleclass.class(...),生成类的表。
setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })
middleclass.class(...)中调用了成生有继承的类super:subcalss与生成单独类的_includeMixin(_createClass(name), DefaultMixin),
_createClass(name)这个方法是用来创建一个类的基本结构或者说可以理解为一个抽象类:
local aClass = { name = name, super = super, static = {},
__instanceDict = dict, __declaredMethods = {},
subclasses = setmetatable({}, {__mode='k'}) }
属性有:
- name 类名
- super 父类的table
- static 静态表
- __instanceDict 存放类定义的属性和函数(包含父类)
- __decaredMethod (当前类声明的方法,不包含父类)
- subclasses 子类表, 使用弱引用
先设置aClass.static表的元表为 __index = __instanceDict(或者super.static),然后设置aClass元表__index 为 static以及__tostring与__call元方法。这里的__call元方法时调用的类中的new方法。
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
setmetatable(aClass, { __index = aClass.static, __tostring = _tostring,__call = _call, __newindex = _declareInstanceMethod })
使用_includeMixin这个方法为我们的类做一个具体的填充。这里使用到一个DefaultMixin的表。我们可以把这个表理解为来实现上面我们构造出来的抽象类。
DefaultMixin提供如下方法:
- __tostring:默认tostring方法
- initialize:初始化方法 类似于默认构造函数
- isInstanceOf:判断是否是一个类的实例
静态方法: - allocate:创建一个实例表
a.添加class属性,指向类表
b.将类表的__instanceDict设置为元表
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,
●new:调用allocate与initialize方法,实例出来对象并调用构造方法。
●subclass:创建子类
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)//创建一个子类的抽象类。
//把父类实例中的所有方法赋给子类使用_propagateInstanceMethod方法。
for methodName, f in pairs(self.__instanceDict) do
_propagateInstanceMethod(subclass, methodName, f)
end
//设置子类的默认构造方法。
subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end
//在自己的subclass索引中,添加子类的索引
self.subclasses[subclass] = true
//调用自己的subclassed方法,参数为子类
self:subclassed(subclass)
return subclass
end
●isSubclassOf:是否是指定类的子类。
●include:将参数合并到本类中,调用的_includeMixin方法。
_includeMixin:使用这个方法把填充之前构造的抽象类,分别把静态与非静态的方法与参数都赋值给抽象类。
_declareInstanceMethod:在类中添加声明方法和属性
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
- 注册到__declaredMethods
- 如果f为nil,则去父类取该字段
- 将域添加到子类中
_propagateInstanceMethod:添加域到表中,并添加到所有的子类中,相当于继承
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, 调用_createIndexWrapper
- 将f添加到aClass.__instanceDict[name]中
- 遍历所有子类,如果子类不包含该方法,则添加到子类中(若包含,相当于重写)
_createIndexWrapper:对__index处理
总结
- __instanceDict 记录当前类,以及所有父类定义的实例方法(属性), __index指向自己
- __declaredMethods 记录当前类声明的方法(属性)
- subclasses 当前类的所有子类,弱引用
-
static 静态表,定义new, include, isSubclassOf等方法,__index指向__instanceDict。
实例变量不能直接访问static表,必须通过.class.static访问。
类的静态方法可以通过class.static:func 来定义 -
类的默认表 定义__instanceDict,__declaredMethods, static等属性。
__index指向static表,可以直接使用static中的字段(A:new())。
__newIndex为_declareInstanceMethod方法,添加字段时会更新__instanceDict以及__declareMethods - 实例表 定义class属性,指向当前的类。 metatable为对应类的__instanceDict
网友评论