Lua的OOP实现

最近一直没在偷懒,在收集各种开源项目中OOP的实现方案。关于其中的理解,因为给新人指导,就要求自己能够很好的理解,并讲解出来。教会也是给对于知识理解的更高阶段。

一、实现OOP

二、lua 的作者推荐了一种方案来实现 OO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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面向对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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

来源于chukong的quick-cocos2d-x

五、xlua-framework 中的 class 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
-[[
-- 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),但是从使用的角度来说,因为在访问数据和方法速度上,第二种方式还是有优势的,所以,在具体的使用的时候,可以灵活一下使用,将两者结合一下。

比如说,对于客户端这边来说,类在开始创建好了以后就一般不需要修改了,而且子类一般都是需要父类的所有的方法和数据的,所有我们就可以使用第二种方式,而生成对象实例的时候,对象的实例一般都不会调用类的所有的方法,而且用完了这个实例,就会销毁的,所以,我们这里就可以采用第一种方式。

  • 基于以上的想法进行改进,这样用于作为所有界面的基类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    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上解答了我很多问题,给予方向上的指点
文章目录
  1. 1. 一、实现OOP
  2. 2. 二、lua 的作者推荐了一种方案来实现 OO
  3. 3. 三、云风实现的Lua面向对象
  4. 4. 四、cocos2dx-lua 中的 class 实现
  5. 5. 五、xlua-framework 中的 class 实现
  6. 6. 六、2种实现方式的总结
  7. 7. 七、最后的总结
  8. 8.
|