利用动态脚本编写你的Java应用程序以及重用你的Java类库
自从计算机诞生以来,软件开发就倾向于使用高级语言进行开发。从汇编,到C,到C++,再到JAVA,每一次升级就会面临来自各界同样的问题:太慢、而且有太多的Bug、开发者不想放弃对这些原有语言的使用。渐渐地,随着硬件的快速发展,新的研究和开发技术大大改进了编译器、解释器、和虚拟机,开发者不得不向高级语言转移,放弃他们使用的低级语言开发以提高生产力(将他们从低级语言的障碍中释放出来以提高他们的生产力)。
Java现在在软件开发的很多领域里面占有主导地位,但是在这个发展过程中,动态脚本很有可能无情地取代它的地位。许多年以来,像Python、Perl、Rexx、Groovy、TCL和Ruby这样的语言能够在很多专业领域里面非常出色地工作,例如文件处理、自动测试、软件构建、代码重构、和Web图形页面设计——他们有着历史性的名字“脚本语言”。而且在最近的一些年里,在大多数由Java,C++和其他编译型计算机语言开发的大型工作里面,他们也取得了相应的进展。
去年的时候,Ruby on Rails(RoR)Web框架使Ruby有了更进一步的发展。RoR结构利用简单的Ruby代码定义了一个典型的多层次Web应用程序——图形页面层、业务逻辑层和数据持久层,因此减小了冗余文件、样本文件代码、生成的源代码以及配置文件。RoR框架能够更加优化更加轻易地使用Ruby语言;而且Ruby,这种完善的脚本语言,相对于RoR框架来说可以在更多的领域里面使用。
作为一个长期的Java开发者,我很可能坚持在一段时间里一直用Java作开发。但是我仍然保持在我开发的基于Java的系统里面使用其他的语言,而且Ruby最近显示出来是非凡好的一种候选语言。在JRuby解释器的帮助下,Ruby和Java一起工作得很好,包括配置、整合、和Java软件的重用。而且在简单学习Ruby的过程中也提高了我Java代码的质量。使用Ruby可以让我很轻易地完成像功能程序和元程序一样的技术手法,这些技术手法我在Java里面都是很难实现的。学习这些Ruby里面的技术手法可以帮助我更好鉴别什么时候而且怎样在Java开发中使用它。
这篇文章,我希望能够和你一起分享我在开发Java系统的时候使用Ruby的那种兴奋感。我比较一下Java和Ruby的优点和缺点,而且介绍一下JRuby解释器的支持者和反对者。而且我会向大家显示区分Ruby和Java使用的最佳实践以让它们各自得到最优化的使用。我会使用一些简单的代码来举例说明这个观点,并且介绍一个消息实例来展示在Java系统里面怎样结合使用Ruby,使其能够更好地使用动态元程序语言的弹性、表现方式以及功能。
Ruby vs. Java
这篇文章从一个Java开发者的角度解释了Ruby,主要是集中比较这两种语言。像Java一样,Ruby也是一种完全的面向对象的语言。但是这两种语言有很大的不同。Ruby是动态类型的而且是在源代码解释器里面运行的,这种语言能够像程序和功能范例一样支持元编程。我这里不会介绍Ruby的具体语法,接下来的文章里面会广泛地覆盖其他各个方面。
动态类型
Java有静态类型。你定义每个变量的类型,接下来在编译的过程中,假如你使用了类型错误的变量将会得到一个编译时错误。Ruby却相反,拥有动态类型:你不用定义函数和变量的类型,而且没有到运行的时候不会使用类型检测,假如你调用一个不存在的方法就会得到错误信息。尽管这样,Ruby不会关心一个对象类型,仅仅看它是否在一个方法里面调用了这个对象的方法。因为这个原因,这种动态方法可以得到这样一个dUCk类型:“假如一个事物走起来像一只鸭子(duck)而且像一只鸭子(duck)呷呷地叫,它就是一只鸭子。”
Listing1.Duck typing
class ADuck
def quack()
puts "quack A";
end
end
class BDuck
def quack()
puts "quack B";
end
end
# quack_it doesn't care about the type of the argument duck, as long
# as it has a method called quack. Classes A and B have no
# inheritance relationship.
def quack_it(duck)
duck.quack
end
a = ADuck.new
b = BDuck.new
quack_it(a)
quack_it(b)
Java也可以通过反射让你使用动态类型,但是这种笨拙冗长的工作会导致很多混乱的异常发生,像NoSuchMethodError和InvocationTargetException;在实践中,这些异常倾向于在Java反射的代码中忽然出现,而且相对于Ruby而言出现频率更高。
即使在没有使用反射的Java代码中,你会经常丢失掉静态类型的信息。比如,在Command设计模式里面使用execute()方法必须返回Object胜于在Java代码里面使用的非凡类型,结果会导致很多ClassCastException发生。同样的,当在编译时和运行时修改方法签名的时候,运行时错误就会发生。在实践开发中,不论是Java还是Ruby,这样的错误很少引起严重的程序Bug。一个健壮的单元测试——任何时候你都会用到的——通常都能够及时捕捉他们。
Ruby的动态类型意思是你不用重复问你自己一个问题:在Java里面你是否经常在一行里面碰到这样冗长的代码:
XMLPersistence xmlPersistence
= (XMLPersistence)persistenceManager.getPersistence();
Ruby消除了这种对于类型定义和转换的需要,上边的代码用一个典型的Ruby等价表达为;
xmlPersistence = persistence_manager.persistence.
Ruby的动态类型意义上不是弱类型——Ruby经常需要你传递正确类型的对象。事实上,Java强制类型转换比Ruby要弱。例如,Java里面:”4”+2 等于”42”,这里会将整数转化为字符串,在Ruby里会抛出一个TypeError,告诉你这个“can't convert Fixnum into String.”(Fixnum类型是不可以转化为String的)。同样的,Java里,因为作类型校正牺牲了速度,而且过多地做了整型操作,产生像Integer.MAX_VALUE + 1的整型,和Integer.MIN_VALUE等价,可是Ruby类型校正整型只是在需要的时候。
不论Ruby有什么优点,Java的静态类型可以让它在大规模的项目里面作为首选:Java工具能够在开发时候明白代码意思。IDE能够在类之间依靠跟踪,找到方法和类的用处,自动检标识符而且帮助你检测代码。同样的虽然Ruby工具在这些功能上存在限制,它缺乏类型信息所以不能够完成上边这些工作。