内容


轻量级开发的成功秘诀,第 7 部分

Java 替代方案

Java 技术之外的轻量级开发

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 轻量级开发的成功秘诀,第 7 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:轻量级开发的成功秘诀,第 7 部分

敬请期待该系列的后续内容。

我在科罗拉多的一条宽阔的大河上。习惯了在陡峭、狭窄的小河上涉水,我发现这种体验会让人丧失勇气,而且并不雅观。尽管我划过大量的 V 级急流,但这条仅 III 级的河流却起初让我有些惊恐。最终,我学会了利用河流的力量来达到我的目的。我还发现,我划过的河流越多,用来适应的时间就越少。

编程也是如此。您也许能够轻松地用您自己的方法解决 Java 编程的问题,但发现其他的语言是如此的笨拙。在本文,我将带您离开舒适地带。

本系列的前几篇文章都局限于 Java 编程。但在本文中,我会讨论一个异端想法:有时,Java 编程语言有损生产力。

简史

在给 Java 技术飞艇戳几个洞之前,我应该提醒您一点儿历史。Java 编程语言来自一个没有希望的来源(Sun Microsystems),为了与控制服务器端的统治语言(C++)竞争,那时一个程序设计范例正在寻求摆脱困境的办法(过程客户端 - 服务器代码)。互联网爆炸,突然带有内置 Java 虚拟机(JVM)的 Netscape 出现在每个桌面上。为了被广泛接受,Java 语言向 C++ 社区做出了几个重大妥协:

  • 像 C++ 一样,它是静态类型,而不是像 Samlltalk 那样的动态类型。
  • 像 C++ 一样,它允许原语和对象。
  • 它涵盖了 C++ 的语法和控制结构。

为了获得市场,Sun 保留了与 C++ 足够接近的东西来吸引社区。Java 语言没有必要比所有其他的面向对象语言都好。它只需比 C++ 好就行了。现在,其中的一些妥协开始损害 Java 语言。

闭包

Java 语言允许比 C++ 更高的抽象,比如管理内存和字符串。但是,要达到像 Ruby 或 Smalltalk 语言那样高的抽象,Java 语言还有很长的路要走。以迭代为例:用 Java 技术来完成一个数组的迭代,要这样写:

for(i=0;i<array.size();i++) {
  System.out.println(array[i]);
}

在 Ruby 中,要这样写:

array.each {|element| puts element}

您没必要为迭代如何进行而担心,因此花费的工作量要少得多。您只是把花括号(我们把这个块叫做 闭包(closure))中的代码传递到数组中,每个方法都知道如何完成迭代。需要迭代器的 Java 集合比我在这里给出的例子更糟糕,因为您被迫声明和使用独立的迭代器。当然,Ruby 也有循环和迭代器,但您不需要使用它们,同样因为有一个以闭包的形式存在的更高的抽象。

闭包不只是用来迭代的。它们也使得许多问题都易于解决:

  • 以一个代码块遍历数据库是容易的:
    dbh.execute("SELECT * FROM words") do |s|
      s.fetch_array do |row|
        puts row.join(", ")
      end
    end
  • 划分事务是自然的:
    Account.transaction(david, mary) do
      david.withdrawal(100)
      mary.deposit(100)
    end
  • 可以在共享代码的主体中隐藏资源管理和异常的冗长细节。使用该策略,文件处理会更加简洁。

单独使用闭包,可以大大限制代码行的数量。当然,可以在 Java 语言中以更多的代码使用匿名代码块来模仿闭包,但这种技术写起来和读起来都有些笨拙。

延续

延续(continuation)是另一个抽象,它变得越来越重要。延续让您能够在任何时刻及时捕获执行状态(使用实例变量和调用栈)。您可以在晚些时候及时返回那一点。对 Web 服务器来说,也许希望在询问用户信息之前捕获一个延续。然后,当用户返回控制权到应用程序服务器时,就可以恢复延续。使用延续服务器,比如 Seaside 和 Iowa,可以编写无状态的 Web 应用程序,就像编写任何其他有状态的用户界面一样。

清单 1 显示的是 Seaside 中购物车的 checkout 方法的一个例子。

清单 1.Seaside 中的 checkout 方法
go
  | shipping billing creditCard |
  cart _ WAStoreCart new.
  self isolate:
     [[self fillCart.
    self confirmContentsOfCart]
      whileFalse].
  self isolate:
     [shipping <- self getShippingAddress.
      billing <- (self useAsBillingAddress: shipping)
                    ifFalse: [self getBillingAddress]
                    ifTrue: [shipping].
    creditCard <- self getPaymentInfo.
    self shipTo: shipping billTo: billing payWith: creditCard].
  self displayConfirmation.

对您来说,这个 Smalltalk 代码可能看起来并不自然。妙处在于该方法调用向用户请求输入的任务。基于用户的反应,应用程序的流程发生改变。例如,如果您要询问一个单独的开单地址,就将得到另外的开单窗口。多页面的购物车结帐被减少到一个单页面的方法。

延续的管理在幕后悄悄进行。您不需要知道有关状态的任何东西。与此相比,通常的 Java 语言替代方案看起来是残酷的。每次处理一个请求的时候,您都需要决定保存哪些数据以及在哪里保存它。像 Cocoon 这样的框架模仿这种控制,但没有延续,它会更加困难,而且不能远程得到任何与 Seaside 的功能相似的东西。

元编程

元编程(Metaprogramming),即写程序的程序,是一个功能强大的概念。您已经看过 Java 语言的两个例子:

  • 在 Hibernate 中,元编程以字节码增强和反射机制来给对象增加持久性,而不迫使您写 SQL 查询。
  • 在 Spring 中,元编程允许给传统的 Java 对象(POJO)增加服务,而不会使它们变得凌乱。

事实上,Java 社区花费越来越多的时间在元编程上面,力求提供更好的服务透明性,但对元编程来说,Java 编程语言并不是一种特别好的语言。在其他语言中,元编程甚至更加重要。在 Ruby on Rails 框架成长的背后,Ruby 提供了爆炸性的力量。Rails 的革新已经横扫了这个产业,因为元编程允许框架用户通过非常少的工作建立令人难以置信的功能。该框架使用命名惯例、智能缺省和元编程来找到一个关系数据库表格的内容,然后动态地建立一个模型,在这个模型中对每个数据库列都有一个属性。这个过程发生在运行时,因此数据库模式的更改能够在用户界面得到反映,如果您希望如此的话。

如同在 Java 语言中一样,您可以找到元编程框架来处理持久性、事务和 XML。事实上,如您所见,在本系列的前三篇中,越来越多的 Java 开发人员在寻找提供更好的透明性的工具。当然,为了这样做,编码人员必须在 Java 的本来目的之外伸展 Java 语言。为了得到更好的透明性,需要使用一些技术,比如代码生成、字节码增强、代理、拦截器、反射和面向方面的编程(AQP)。当创建更加复杂的企业级应用程序时,像 Spring、Hibernate、JBoss 应用程序服务器和 HiveMind 之类的框架越来越多地使用这些技术来提供更好的透明性。

通过提供更好的反射和类的开放结构,Lisp、Python、Ruby 和 Smalltalk 等语言都使透明性的实现更加容易。让我们看看 Ruby 的两个例子。

反射

反射(Reflection) 是使用 API 找到给定对象基本构建块的能力。在 Ruby 中,反射是很容易的。一切都是对象,一切都是类。可以向任何类询问它的类、它的父类或它所支持的方法。例如,可以使用代码来向数字 4 询问它的类和它所支持的方法:

irb(main):001:0> 4.class
=> Fixnum
irb(main):002:0> 4.methods
=> ["%", "between?", "send", "<<", "prec", "modulo", "&", 
"object_id", ">>", "zero?", "size", "singleton_methods", 
"__send__", "equal?", "taint", "id2name", "...and so on.

在 Ruby 中,使用反射结果是容易的。为了在一个对象上调用 to_s(转换为字符串)方法,只需发送一个字符串给它:

irb(main):003:0> meth="to_s"
=> "to_s"
irb(main):004:0> i=4
=> 4
irb(main):005:0> i.send meth
=> "4"

嗯,您可以看到简化的反射。为使事情更加容易,不需要担心原语或枚举、数组或泛型的特殊情况。这是纯自然的面向对象的天堂。Java 的例子比较繁琐。但它们是必须的,因为需要用反射来取得更好的透明性。

Ruby 在另一个地方很吸引人。在 Ruby 中,能够挂到一个对象的任何部分。例如,拦截器很容易实现。因为 Ruby 允许在运行时在类或对象上面修改方法,可以简单地重命名一个现有的方法并且以旧名字插入一个新的方法。以下的极端例子拦截 Class 类里的新方法,它基于 Dave Thomas 的 Programming Ruby 一书中的一个例子:

class Class
  alias_method :original_new, :new
  def new(*args)
    result = original_new(*args)
    print "Interceptor on Class.new "
    return result
  end
end

可以使用相同的技术来插入新方法或改变旧方法的行为。AOP 和依赖注入在像 Ruby 这样的动态语言中未受到关注,主要因为它们是不必要的。

其他因素

除了反射和透明性之外,Java 语言还有一些其他的限制因素。单独来看,这些局限性没有一个是重要的。但合到一起,就不是这样了:

静态类型

如果您希望自动化的单元测试捕获许多差错,就必须声明每一个变量和每一个参数,这要求更多的时间,但得到的好处比您想像的要少得多。

不完全的面向对象

编写反射代码的时候,必须处理所有的异常,比如原语和数组。

较长的反馈周期

许多其他的 Web 框架允许改动代码并保存,然后在您的浏览器中单击 重新装入 来立即查看改变。

字面结构化数据

Java 语言不能很好地表示结构化的数据,因此到处都可以看到 XML —— 甚至是在没有附加值的地方。

简而言之,Java 语言不是一种生产效率非常高的程序语言。创建者做了一些聪明的妥协来从 C++ 夺取控制权,但我们开始为那些妥协付出代价了。在 Beyond Java 一书中,我详细讨论了这些问题。我仍然为大多数的项目使用 Java 语言。很容易找到适合我需要的框架。我能很快地找到程序员和工具。它是一种经过验证的编程语言。我的许多客户有太多的遗留代码,修改起来工作量太大。

但是,我已经开始为某些已付项目使用不同的语言。对于大型关系数据库上的基于 Web 的用户界面,Ruby on Rails 是有效的。对于要求有限的可伸缩性和可用性但要求复杂导航的应用程序,Seaside 是有效的。许多语言处理丰富的客户端开发要比 Java 语言好。

我还发现,如果赢利较大,处于创业阶段的公司愿意承担更多的风险。如果生产效率是首要关心的问题,团队很小,并且您正在解决一个很适合于动态语言的问题,那么使用动态语言是很有意义的。通过迁移到一种更加动态的语言和一个更加适合项目的框架,我为一个客户节省了 60% 的项目预算。通过迁移到一种技能要求没有 Java 语言那么严格的语言,我让另一个客户减少了 30% 多的职员。

底线?有时,当考虑轻量级开发时,值得脱离 Java 语言看一看。在接下来的几篇文章中,我将离开 Java 语言来探索一种新的语言和几个轻量级框架。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source, Java technology
ArticleID=98245
ArticleTitle=轻量级开发的成功秘诀,第 7 部分: Java 替代方案
publish-date=11072005