Lua OO
Lua 面向对象编程
Lua的面向对象要映射到table上
进入代码解析:
------------------------------------------------------------------------------------
1.-------------------先引用一下Sputnik中的实现片断:
local Sputnik = {} local Sputnik_mt = {__metatable = {}, __index = Sputnik} function new(config, logger) -- 这里生成obj对象之后,obj的原型就是Sputnik了,而后面会有很多的Sputnik的方法定义 local obj = setmetatable({}, Sputnik_mt) -- 这里的方法就是“继承”的Sputnik的方法 obj:init(config) 返回这个对象的引用 return obj end
由上面可见,两个表定义加上一个方法,实现了类,及由类产生对象的方案。因为这是在模块中,故new前面没有表名称。这种方式实现有个好处,就是在外界调用此模块的时候,使用
sputnik = require "sputnik" --然后,调用 s = sputnik.new() --就可以生成一个sputnik对象s了,这个对象会继承原型Sputnik(就是上面定义的那个表)的所有方法和属性。
但是,这种方法定义的,也有点问题,就是,类的继承实现上不方便。它只是在类的定义上,和生成对象的方式上比较方便,但是在类之间的继承上不方便。
2.-------------------下面,用另一种方式实现:
A = { x = 10, y = 20 } function A:new( t ) local t = t or {} self.__index = self setmetatable( t, self ) return t end
从A中产生一个对象AA
AA = A:new() --此时,AA就是一个新表了,它是一个对象,但也是一个类。
它还可以继续如下操作:
s = AA:new() --AA中本来是没有new这个方法的,但它被赋予了一个元表(同时也是原型)(setmetable),这个时候是A,A中有new方法和x,y两个字段。
AA通过__index回溯到A找到了new方法,并且执行new的代码,同时还会传入self参数。这就是奇妙所在,此时候传入的self参数引用的是AA这个表,而不再是第一次调用时A这个表了。因此 AA:new() 执行后,同样,是生成了一个新的对象s,同时这个对象以AA为原型,并且继承AA的所有内容。至此,我们不是已经实现了类的继承了吗?AA现在是A的子类,s是AA的一个对象实例。后面还可以以此类推,建立长长的继承链。
3.---------类和原型
由上也可见,类与原型概念上还是有区别的,Lua是一种原型语言,这点体现的得很明显,类在这种语言中,就是原型,而原型仅仅是一个常规对象。
下面,如果在A中定义了函数:
function A:acc( v ) self.x = self.x + v end function A:dec( v ) if v > self.x then error "not more than zero" end self.x = self.x - v end
然后,现在调用
s:acc(5)
那么,是这样调用的,先是查找s中有无acc这个方法,没有找到,然后去找AA中有无acc这个方法,还是没找到,就去A中找有无此方法,找到了。找到后,将指向s的self参数和5这个参数传进acc函数中,并执行acc的代码,执行里面代码的时候,这一句:
self.x = self.x + v
在表达式右端,self.x是一个空值,因为self现在指向的是s,因此,根据__index往回回溯,一直找到A中有一个x,然后引用这个x值,10,因此,上面表达式就变成
self.x = 10 + 5
右边计算得15,赋值给左边,但这时self.x没有定义,但是s(及s的元表)中也没有定义__newindex元方法,于是,就在self(此时为s)所指向的表里面新建一个x字段,然后将15赋值给这个字段。
经过这个操作之后,实例s中,就有一个字段(成员变量)x了,它的值为15。
下次,如果再调用
s:dec(10)
的话,就会做类似的回溯操作,不过这次只做方法的回溯,而不做成员变量x的回溯,因为此时s中已经有x这个成员变量了,执行了这个函数后,s.x会等于5。
综上,这就是整个类继承,及对象实例方法引用的过程了。不过,话还没说完。
-------------------------------------------------------------------------------------------------
AA作为A的子类,本身是可以有一些作为的,因为AA之下的类及对象在查找时,都会先通过它这一关,才会到它的父亲A那里去,因此,它这里可以重载A的方法,比如,它可以定义如下函数:
function AA:acc(v) ... end function AA:dec(v) ... end
函数里面可以写入一些新的不一样的内容,以应对现实世界中复杂的差异性。这个特性用面向对象的话来说,就是子类可以覆盖父类的方法及成员变量(字段),也就是重载。这个特性是必须的。
AA中还可以定义一些A中没有的方法和字段,操作是一样的,这里提一下。
Lua中的对象还有一个很灵活强大的特性,就是无须为指定一种新行为而创建一个新类。如果只有一个对象需要某种特殊的行为,那么可以直接在该对象中实现这个行为。也就是说,在对象被创建后,对象的方法和字段还可以被增加,重载,以应对实际多变的情况。而毋须去劳驾类定义的修改。这也是类是普通对象的好处。更加灵活。
可以看出,A:new()这个函数是一个很关键的函数,在类的继承中起了关键性因素。不过为了适应在模块中使用的情况(很多),在function A:new(t)之外还定义一个
function new(t) A:new(t) end
将生成函数封装起来,然后,只需使用 模块名.new() 就可以在模块外面生成一个A的实例对象了。
差不多了吧,可以看到,这种类实现的机制是多么自洽,简洁,灵活,强大!
----------end!!----------------------
浙公网安备 33010602011771号