Appearance
lua面向对象
上篇文章简单实现了面向对象功能,这次,我们来说说实现lua继承及添加构造函数
先理解一个概念self
lua
local cc = class()
function cc:test()
self.d1 = 1
end
function cc.test(self)
self.d1 = 2
end
return cc
按照上篇文章所说个人习惯,将上述代码保存为test.lua
lua
local test = require 'test'
local d = test.new()
d:test()
print(d.d1) --2
你可能会疑问 test文件中到底调用了哪个test方法呢,self又是从哪里来的呢?
其实test文件中的2个test方法完全是等效的,只不过我们使用 : 时,隐式的传了一个参self (即d:test()传了一个参数d本身),所以也可以写作d.test(d)
所以self其实只是一个传递的参数而已,它并不等于cc本身(在这里,self其实是obj,而cc则是_class)
实现lua的继承
lua
function class(super)
local _class = {__mode='kv'} --class对象 ?__mode弱表引用 应该要设置元表吧?
_class.super = super
_class.new = function(...)
local obj = {} --new出来的对象
local create
create = function(c,...)
if c.super then
create(c.super,...)
end
if c.ctor then
c.ctor(obj,...)
end
end
create(_class,...)
setmetatable(obj,{__index=_class}) --指向_class的成员方法
return obj
end
--实现单例模式
_class.instance = function(...)
if not _class._instance then
_class._instance = _class.new(...)
end
return _class._instance
end
if super then --若父类存在,指向父类的成员方法
-- setmetatable(_class,{__index=_class.super}) --这里有个bug,当子类无构造函数时,会导致父类构造函数执行多次,暂时修改为下面代码(2018/03/17)
setmetatable(_class,{__index = function(t,k)
if k == 'ctor' then
return t[ctor]
else
return _class.super[k]
end
end})
end
return _class
end
这次直接贴完整代码了,上面代码完整的实现了构造函数、方法继承,顺便还添加了个常用的单例模式
- new方法中的递归方法create,主要目的是执行所有父类的构造函数ctor,因为ctor传参为obj,所以在ctor中所有定义的self.字段名 其实即为obj的变量
- super是父类,当父类存在时,需要将_class的元表指向父类,保证我们找到父类的成员
- 单例就不用说了,几行简单的代码,应该都能看得懂
测试是否实现了继承
先贴下代码 test1.lua (test1继承test2,test2继承test3)
lua
require 'class'
local test2 = require 'test2'
local cc = class(test2)
function cc:ctor(d)
print('test ctor self:'..tostring(self)..' cc:'..tostring(cc))
end
function cc:test()
self.p_test = 'test'
-- self.super.test(self)
-- self.super:test()
-- cc.super:test()
cc.super.test(self)
print('test execute self:'..tostring(self)..' cc:'..tostring(cc)..' self.super:'..tostring(self.super))
end
return cc
test2.lua
lua
local test3 = require 'test3'
local cc = class(test3)
function cc:ctor()
print('test2 ctor self:'..tostring(self)..' cc:'..tostring(cc))
end
function cc:test()
self.p_test2 = 'test2'
-- self.super.test(self)
-- self.super:test()
-- cc.super:test()
cc.super.test(self)
print('test2 execute self:'..tostring(self)..' cc:'..tostring(cc)..' self.super:'..tostring(self.super))
-- print('p_test:'..self.p_test)
-- print('p_test3:'..self.p_test3)
end
return cc
test3.lua
lua
local cc = class()
function cc:ctor()
print('test3 ctor self:'..tostring(self)..' cc:'..tostring(cc))
end
function cc:test()
self.p_test3 = 'test3'
print('test3 execute self:'..tostring(self)..' cc:'..tostring(cc))
end
return cc
测试代码
lua
local test = require 'test1'
local d = test.new(11)
d:test()
执行测试代码,运行ok,做下解释(这就需要你能理解我前面所讲解的self概念了) 你可能看到了在调用父类方法时,有四种写法
- self.super.test(self)
- self.super:test()
- cc.super:test()
- cc.super.test(self)
其实2,3,4的写法都是ok的,都可以正常调用父类的方法,但关键在于我们需要在各自方法中访问self,这就会造成问题。我们所期待的继承关系是 子类可以访问父类的成员,父类同样可以访问子类的成员。
- 分析1:因为调用d:test(),所以test1中的self其实是d本身,也就是new出来的对象obj,self.super其实是拿obj.super,没有则从_class中取,可以正常调用test2中的test方法,然而test2中拿到的self.super还是test2本身,造成循环调用,堆栈溢出
- 分析2:我们把test2中的print解开,会发现提示找不到self.p_test。 因为p_test是在test1.lua中定义的,所以只属于test1,而在test2中拿到的self是test2本身,并不存在p_test字段。
- 分析3:3其实同2是一样的,都属于可以访问到父类的成员,却无法访问到子类的成员。
结论:因为我们需要在各自的方法体里准确找到其父类,所以 采用 cc.super
因为我们需要在父类中访问子类的成员,所以可以将所有访问的对象都设置为子类,所以将子类当参数传递 即cc.super.test(self)