-
2006-11-06
管窥Ruby——方法定义
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://dreamhead.blogbus.com/logs/3776107.html
在C/C++中,函数虽然不像functional语言中一样,拥有first class的地位,但函数指针的存在,还是让它在一些表达上显得很简洁。在C层次上,定义一个Ruby方法的API是这样的:
void rb_define_method( VALUE classmod, char *name, VALUE(*func)(), int argc )它为类或模块(classmod)定义了一个实例方法,在Ruby层次上的参数名为name,参数个数为argc。通过这个函数,一个Ruby层次上的函数就和C层次上的函数连接到了一起,如同我在《管窥Ruby——Allocator》中谈到两个层次对象之间的关系一样。
下面是一个例子,它定义了Array#length方法:
rb_define_method(rb_cArray, "length", rb_ary_length, 0);
(array.c)如果把同样的问题映射到Java之中,又会是怎样的情形呢?
我们知道,在Java中没有指针的概念,所以,我们不可能像C语言这样,直接用函数(实际上是函数指针)作为参数。通常的替代方案是使用接口(或是基类)作为参数。类似于上面的功能,我们可能会这样定义接口:
void defineMethod(RubyClass klassmod, String name, RubyMethod method, int argc);当我们只有一个方法要实现的时候,或许这种方式与C的方式看不出来什么差别。当我们有铺天盖地的方法要实现,这种定义的差异便显现出来。
通常,我们会把相关的内容实现在一个类里:
class K {
void methodA() {}
void methodB() {}
void methodC() {}
}但这样的实现并不能让它和前面的定义友好协作,因为前面那个定义调用一次,只能加入一个方法。
defineMethod(RubyKClass, "methodA", ???, 0);若要把所有的方法都加入其中,仅仅让K继承自RubyMethod是不够的,因为它只有一个接口,只能让一个方法起作用,当然,如果打算在接口内部来做dispatch,那又是另外的故事了。
一种方法是加入中间层,让中间层负责完成这个任务。
class MethodAWrapper implements RubyMethod {
... run(...) {
K k = ...
k.methodA();
}
}这样定义就变成了这样
defineMethod(RubyKClass, "methodA", MethodAWrapper, 0);
defineMethod(RubyKClass, "methodB", MethodBWrapper, 0);
defineMethod(RubyKClass, "methodC", MethodCWrapper, 0);这样的解决方案也带来了问题:方法的Wrapper如何来写。因为Wrapper是真正注入进去的内容,所以,有些实现即便K中没有对应实现,只要其接口可以完成,我们一样可以通过Wrapper实现,换句话说,它起到了一个辅助实现的作用,也就是Java编程中常用的Util。所以,有时候,我们会疑问这个方法放在哪实现更好,是方法中还是Wrapper中?再者,虽然Wrapper通常是一个很薄的层次,但毕竟要写,所以,也是一道繁琐的工序。
事实上,JRuby在实现的时候,也有这样的问题。下面是一个定义方法底层的接口:
public void addMethod(String name, ICallable method) ;
(RubyModule.java)这里的ICallable起到的作用与我们前面例子中的RubyMethod是一样的。这个函数是这样调用的:
public void defineMethod(String name, Arity arity, String java_name) {
...
addMethod(name, new ReflectedMethod(this, builtinClass, java_name,
arity, visibility));
}
(AbstractMetaClass.java)ReflectedMethod,看名字就知道,它利用Java中的Reflection。实际上,它正是利用了Reflection对Java的方法进行封装,将Wrapper和方法合二为一,解决我们前面提到的两个问题。
或许,从开发的角度来说,这种解决方案有一定的优越性,因为它大大的减少了编码量,但客观的事实是,Java的Reflection无论如何在性能上也无法达到普通函数调用的水平,而且,在JRuby中,这种函数会大量的调用,所以,这也是JRuby性能为人诟病的一个根本原因之一。
出现这种左右两难的原因是Java语言本身的限制,因为在Java中如果不用Reflection的话,我们无法单独操作方法。显然后来者在这个问题上做得比较好,如果使用C#,delegate会让这个问题的解决方案优雅许多。显然,在继续讨论下去的话,就成了《程序设计语言的表达》的延续。
引用地址:








评论