最近一直没在偷懒,在收集各种开源项目中OOP的实现方案。关于其中的理解,因为给新人指导,就要求自己能够很好的理解,并讲解出来。教会也是给对于知识理解的更高阶段。
一、实现OOP
- 核心是Metatable和__index方法
- 理解OOP中类继承多重继承
可见我上一篇文章Lua的metatable和Lua的面向对象实现
二、lua 的作者推荐了一种方案来实现 OO
function class(def)
local class = {}
local parents = {}
local upv
local env = _G
local wraps
local function super(parent_class)
if not parent_class then
parent_class = parents[1]
end
local this = this
local that = {}
for k, v in pairs(parent_class) do
that[k] = type(v) == 'function' and wraps(this, v) or v
end
return setmetatable(that, that)
end
function wraps(this, func)
return function(...)
local t = env.this
local s = env.super
env.this = this
env.super = super
local ret = pcall(func, ...)
env.this = t
env.super = s
return ret
end
end
function class.__init()
end
for i = 1, math.huge do
inherit, v = debug.getlocal(def, i)
if not inherit then
break
end
local parent_class = _G[inherit]
for i = 1, math.huge do
local name, pclass = debug.getlocal(2, i, 1)
if not name then
break
elseif name == inherit then
parent_class = pclass
break
end
end
if parent_class and type(parent_class) == 'table' then
table.insert(parents, parent_class)
for k, v in pairs(parent_class) do
class[k] = v
end
else
error(string.format('Class "%s" not valid.', name))
end
end
for i = 1, math.huge do
local name, value = debug.getupvalue(def, i)
if not name then
break
elseif name == '_ENV' then
env = value
upv = i
break
end
end
local _env = setmetatable({}, {
__index = function(t, name)
local value = class[name]
return value ~= nil and value or env[name]
end,
__newindex = function(t, name, value)
class[name] = value
end
})
local function senv(env)
if upv then
debug.setupvalue(def, upv, env)
else
_G = env
end
end
senv(_env)
env.pcall(def, env.table.unpack(parents))
senv(env)
return setmetatable({}, {
__ipairs = function()
return ipairs(class)
end,
__pairs = function()
return pairs(class)
end,
__index = function(t, name)
return class[name]
end,
__index_new = function(t, name, value)
class[name] = value
end,
__call = function(...)
local this = {}
for k, v in pairs(class) do
this[k] = type(v) == 'function' and wraps(this, v) or v
end
this.__class = class
this.__init(...)
return setmetatable(this, this)
end
})
end
--Example:
global = true
Inherit = class(function()
this_is_a_property_of_Inherit = true
function __init()
print('Inherit().__init()')
this.init = true
end
function __call()
print('Yay! You\'re calling for me :) init:', this.init, '\n')
end
end)
Example = class(function(Inherit)
property = "Class property"
print('Inherited property:', this_is_a_property_of_Inherit)
print('Global variable: ', global, '\n')
function __init()
print('Example().__init()')
super().__init()
print('this.init:', this.init)
end
function test(...)
print(..., this.__init, '\n')
end
end)
example = Example()
example.test('__init:')
--example()
--
----example.property = 'I\'m a property of instance "example"'
--print('example.property', example.property)
--print('Example.property', Example.property)
SimpleLuaClasses
这个实现方式很特殊,不是那么直白的使用 metatable 去实现,而是通过 debug API 实现的。熟悉 lua 的朋友可能看得出来这个类似与 setfenv,因为只有 lua 5.1 才有 setfenv 和 getfenv,这个是 lua5.3 上的实现。关于 5.3 上的实现可以参考
https://leafo.net/guides/setfenv-in-lua52-and-above.html
三、云风实现的Lua面向对象
local _class={}
function class(super)
local class_type={}
class_type.ctor=false
class_type.super=super
class_type.new=function(...)
local obj={}
do
local create
create = function(c,...)
if c.super then
create(c.super,...)
end
if c.ctor then
c.ctor(obj,...)
end
end
create(class_type,...)
end
setmetatable(obj,{ __index=_class[class_type] })
return obj
end
local vtbl={}
_class[class_type]=vtbl
setmetatable(class_type,{__newindex=
function(t,k,v)
vtbl[k]=v
end
})
if super then
setmetatable(vtbl,{__index=
function(t,k)
local ret=_class[super][k]
vtbl[k]=ret
return ret
end
})
end
return class_type
end
以上是基本的 class 定义的语法,完全兼容 lua 的编程习惯。增加了一个叫做 ctor 的词,作为构造函数的名字。
https://blog.codingnow.com/cloud/LuaOO
四、cocos2dx-lua 中的 class 实现
function class(classname, super)
local superType = type(super)
local cls
if superType ~= "function" and superType ~= "table" then
superType = nil
super = nil
end
if superType == "function" or (super and super.__ctype == 1) then
-- inherited from native C++ Object
cls = {}
if superType == "table" then
-- copy fields from super
for k,v in pairs(super) do cls[k] = v end
cls.__create = super.__create
cls.super = super
else
cls.__create = super
cls.ctor = function() end
end
cls.__cname = classname
cls.__ctype = 1
function cls.new(...)
local instance = cls.__create(...)
-- copy fields from class to native object
for k,v in pairs(cls) do instance[k] = v end
instance.class = cls
instance:ctor(...)
return instance
end
else
-- inherited from Lua Object
if super then
cls = {}
setmetatable(cls, {__index = super})
cls.super = super
else
cls = {ctor = function() end}
end
cls.__cname = classname
cls.__ctype = 2 -- lua
cls.__index = cls
function cls.new(...)
local instance = setmetatable({}, cls)
instance.class = cls
instance:ctor(...)
return instance
end
end
return cls
end
五、xlua-framework 中的 class 实现
-[[
-- added by wsh @ 2017-11-30
-- Lua面向对象设计
--]]
--保存类类型的虚表
local _class = {}
-- added by wsh @ 2017-12-09
-- 自定义类型
ClassType = {
class = 1,
instance = 2,
}
function BaseClass(classname, super)
assert(type(classname) == "string" and #classname > 0)
-- 生成一个类类型
local class_type = {}
-- 在创建对象的时候自动调用
class_type.__init = false
class_type.__delete = false
class_type.__cname = classname
class_type.__ctype = ClassType.class
class_type.super = super
class_type.New = function(...)
-- 生成一个类对象
local obj = {}
obj._class_type = class_type
obj.__ctype = ClassType.instance
-- 在初始化之前注册基类方法
setmetatable(obj, {
__index = _class[class_type],
})
-- 调用初始化方法
do
local create
create = function(c, ...)
if c.super then
create(c.super, ...)
end
if c.__init then
c.__init(obj, ...)
end
end
create(class_type, ...)
end
-- 注册一个delete方法
obj.Delete = function(self)
local now_super = self._class_type
while now_super ~= nil do
if now_super.__delete then
now_super.__delete(self)
end
now_super = now_super.super
end
end
return obj
end
local vtbl = {}
_class[class_type] = vtbl
setmetatable(class_type, {
__newindex = function(t,k,v)
vtbl[k] = v
end
,
--For call parent method
__index = vtbl,
})
if super then
setmetatable(vtbl, {
__index = function(t,k)
local ret = _class[super][k]
--do not do accept, make hot update work right!
--vtbl[k] = ret
return ret
end
})
end
return class_type
end
xlua-framework
xlua-framework/Assets/LuaScripts/Framework/Common/BaseClass.lua
六、2种实现方式的总结
- 1、使用metatable的__index域实现,实现的时候需要利用的lua语言的一些特性才可以实现
- 优点:
- 1、由于子类的很多数据和方法都是共用了父类的,用到父类的数据和方法的时候,只是在使用的时候才直接调用父类的方法和数据,这样可以减少程序内存的消耗,更主要的是,父类在运行期的修改是会影响到子类的;
- 2、充分利用了lua语言的特性,父类的方法和数据的访问是解析器来做的,所以效率上的开销还是比较小的;
- 缺点:
- 1、如果父类中有一个数据是一个引用的时候(如table)的时候,就会出现在一个子类中操作这个table会改变其他子类的情况,造数据的不一致,所以应该尽量避免这种类的创建,如果有这样的需求的话,就需要对inherit和new函数进行一些特殊的操作,简单来说就是加一个init函数,将所有这类的数据都显示的创建一下。
- 2、由于每次取操作都需要在metatable中取,所以,每次就会增加一层继承,就增加一个函数调用的开销,虽然是由解析器来做的,但是如果层次多了的话,还是有开销的;
- 优点:
- 2、使用table拷贝的方式实现,实现的时候利用的lua的技术为:
- 优点:
- 1、父类中的数据是全部拷贝到子类中的,所以,不存在数据不一致的情况;
- 2、所有的函数调用和数据调用都是直接调用每个实例的,不需要到父类中查找;
- 缺点:
- 1、全部数据的copy,在创建的时候就会增加一个table copy的过程,影响效率;
- 2、全部数据和方法都是在创建的时候拷贝一份的,会增加很多的内存消耗,而且如果在运行期改变了父类,并不能改变子类;
- 优点:
七、最后的总结
结合这两种方式的有缺点,从一个面向对象的角度来说,第一种方式更加适合实现面向对象的特性,第二种方式对面向对象的模拟就牵强一些(缺点2),但是从使用的角度来说,因为在访问数据和方法速度上,第二种方式还是有优势的,所以,在具体的使用的时候,可以灵活一下使用,将两者结合一下。
比如说,对于客户端这边来说,类在开始创建好了以后就一般不需要修改了,而且子类一般都是需要父类的所有的方法和数据的,所有我们就可以使用第二种方式,而生成对象实例的时候,对象的实例一般都不会调用类的所有的方法,而且用完了这个实例,就会销毁的,所以,我们这里就可以采用第一种方式。
- 基于以上的想法进行改进,这样用于作为所有界面的基类
function class(classname,super)
local superType = type(super)
local cls
if superType ~= "function" and superType ~= "table" then
superType = nil
super = nil
end
if superType == "function" or (super and super.__ctype == 1) then
cls = {}
if superType == "table" then
for k,v in pairs(super) do cls[k] = v end
cls.__create = super.__create
cls.super = super
else
cls.__create = super
end
cls.ctor = function(...) end
cls.__cname = classname
cls.__ctype = 1
function cls.New(...)
local ins = cls.__create(...)
for k,v in pairs(cls) do ins[k] = v end
ins.class = cls
ins:ctor(...)
return ins
end
else
if super then
cls = clone(super) --与cocos2dx-lua 中的 class 实现不同之处
cls.super = super
else
cls = {ctor = function(...) end}
end
cls.__cname = classname
cls.__ctype = 2-- luatable
cls.__index = cls
function cls.New(...)
local ins = setmetatable({},cls)
ins.class = cls
ins:ctor(...)
return ins
end
end
return cls
- 所有单例的实现就用第五种的实现
附
- 感谢这篇文章的作者Lua class 的几种实现,不然我这篇文章可能要拖一段时间
- http://www.lua.org/pil/16.html
- lua实现面向对象的特性
- 感谢Misaka Mikoto[https://www.zhihu.com/people/jasonfeeling]在Lua上解答了我很多问题,给予方向上的指点