http://blog.codingnow.com/2006/06/oo_lua.html
在 Lua 中实现面向对象
在 pil 中,lua 的作者推荐了一种方案来实现 OO,比较简洁,但是我依然觉得有些繁琐。
这里给出一种更漂亮一点的解决方案:为了贴代码和修改方便,我把它贴在了 wiki 上。
在这个方案中,只定义了一个函数 class(super) ,用这个函数,我们就可以方便的在 lua 中定义类:
base_type=class() -- 定义一个基类 base_type function base_type:ctor(x) -- 定义 base_type 的构造函数 print("base_type ctor") self.x=x end function base_type:print_x() -- 定义一个成员函数 base_type:print_x print(self.x) end function base_type:hello() -- 定义另一个成员函数 base_type:hello print("hello base_type") end
以上是基本的 class 定义的语法,完全兼容 lua 的编程习惯。我增加了一个叫做 ctor 的词,作为构造函数的名字。
下面看看怎样继承: test=class(basetype) -- 定义一个类 test 继承于 basetype
function test:ctor() -- 定义 test 的构造函数 print("test ctor") end function test:hello() -- 重载 base_type:hello 为 test:hello print("hello test") end
现在可以试一下了:
a=test.new(1) -- 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。 a:print_x() -- 输出 1 ,这个是基类 base_type 中的成员函数。 a:hello() -- 输出 hello test ,这个函数被重载了。
其实,实现多重继承也并不复杂,这里就不再展开了。更有意义的扩展可能是增加一个 dtor :)
ps. 这里用了点小技巧,将 self 绑定到 closure 上,所以并不使用 a:hello 而是直接用 a.hello 调用成员函数。这个技巧并不非常有用,从效率角度上说,还是不用为好。
我认为用 lua 表现 OO ,比 C++ 强,不弱于 ruby 。
btw,lua 是个很优美的语言 IMHO 。lua 应该属于 scheme 的近亲。
使用lua就不应该OO。麻烦而且难于理解。 Lua 不是为OO设计的。
这是叫 override,不过我喜欢把这个词译为重载
我个人认为lua在语言层次实现class意义不大,反而失去了lua的部分灵活性。
我参与的项目里面也实现了lua中的class机制,这里趁机小结一下:p
lua里面实现class机制,比较关键的地方就是class的继承机制,以及class实例化的过程。
class继承机制的关键是怎么让子类拥有父类的方法集:
1.完全使用setMetatable()的方式实现,每继承一次,都把父类的方法集设置为子类方法集的Metatable。这样做应该最符合class 的继承的思想,尽可能的复用逻辑,而且可以做到动态更新父类的方法集后,所有子类都会焙蛳碛姓庀罡碌奶匦?比如满足某些特殊的动态更新需求)。但随着 class继承的层次加深,会生成一个复杂的class方法集table(n层的Metatable),毕竟Metatable是有额外开销的,所以这个 方法不一定是最完美的方案。
2.将父类方法集copy到子类方法集来实现继承,即每当定义一个新的子类时,都将父类中方法集完整copy到子类方法集中。这种方法避免了使用Metatable()带来的额外开销,但却造成了一些数据冗余(其实并不多),并丧失了父类更新子类也会自动更新的特性。
3.方案2的改进版本(也就是云风这里使用的比较强悍的方式:P pf),即同样是采用copy父类方法集的方案,但却改进了copy的机制。将原本在class定义期执行的方法集copy工作转移到实例的运行期间,采 用copy-on-use(等同于copy-on-write的设计思路)的方式,在子类实例用到父类的某个方法时,才将其copy到子类的方法集中。由 于这种copy只发生一次,而且不见得子类会用到父类方法集中的所有内容(事实如此),所以这个方案相对于方案2来说减少了冗余数据,但却几乎没有增加任 何额外开销。
class实例化关键是实例如何享有class的方法集:
1.最烂的方式,方法集copy,即class实例化时,将class的方法集直接copy给实例的数据table。这样的好处就是每个实例创建后,外界 除非直接操作实例的数据table,否则其它行为都不会影响到这个实例的所有属性和特征(也许可以满足某些特殊的需求吧),同时也省掉了一次 Metatable的查找开销。缺点很明显,实例化过程低效,而且产生大量的冗余信息(或者这里也采用copy-on-use的思想? :p)。
2.采用将class方法集设置为实例的Metatable的方式,使实例享有class的方法集(这要求实例的数据类型必须可以拥有自己的 Metatable,即只能是table或userdata)。这个方案更优雅一些,而且符合class的共享思想并且实例化开销很小,应该是实现实例化 的最佳方案了。在实现子类的初始化函数时,一般的思路都是先生成一个父类的实例,再强制将当前子类的方法集设置为这个实例的Metatable。
目前我们采用的class机制就是上述的两个2号方案(考虑更新到云风的3号方案),自己实现了两个接口class_define()、 class_inherit(),其实完成的功能等同于以上代码中的class()。还是很pf lua的作者,估计在设计lua时就考虑到了面向对象的实现问题,很class的多关键机制(如隐式self参数、Metatable机制)都已实现,这 为自己实现class机制提供了强有力的支持。加上脚本的弱数据类型特性,只要思路清晰,在C++中可以实现的class机制lua中基本都能实现一个翻 版,也许下个版本的lua可能会显示支持class:)
所有脚本语言都被人称为scheme的近亲。我还觉得C++的模板也简直就跟Lisp一个样呢