年 月 日
Ruby on Rails 开发和 Java; 开发有着本质的不同在跨越边界 系列的最后一期中Bruce Tate 将概述使用 Rails 从头开发一个复杂可伸缩的 Web 站点时所发现的二者的主要差异
Rails 开发人员常常把 Java 开发人员看作是沉闷而劳碌的老古董而 Java 崇拜者则常常认为 Ruby on Rails 只是一个玩具根本不能用于任何严肃的软件开发作为一名对这两种技术都有着广泛使用经验的顾问我认为真实的情形介于这两种观点之间由于跨越边界 系列文章即将结束因此我打算对它们再作一次比较本文并非考察某种特殊的技术或语言而是主要介绍我当前正在从事的项目并将它与我以前从事的 Java 项目进行比较另外我建议您阅读跨越边界系列的前几期文章对相关主题作更深入的了解这种直接的说明可让您在二者之间权衡利弊并可能使您在数据库 Web 应用程序 greenfield 的开发中通过使用 Rails 获益
业务问题
关于本系列在 跨越边界 系列文章中作者 Bruce Tate 提出这样一种观点当今的 Java 程序员们可以通过学习其他方法和语言更好地武装自己自从 Java 技术明显成为所有开发项目最好的选择以来编程前景已经发生了改变其他框架影响着 Java 框架的构建方式从其他语言学到的概念也可以影响 Java 编程您编写的 Python(或 RubySmalltalk 等语言)代码可以改变编写 Java 代码的方式 本系列介绍与 Java 开发完全不同的编程概念和技术但是这些概念和技术也可以直接应用于 Java 开发在某些情况下需要集成这些技术来利用它们在其他情况下可以直接应用概念具体的工具并不重要重要的是其他语言和框架可以影响 Java 社区中的开发人员框架甚至是基本方式
不论是 Ruby on Rails 框架还是任何 Java 框架都不能解决所有问题为了提高成功的几率需要长期细致地考察业务问题理解周围的各种假定情况以及了解您的团队只有这样才能选出正确的语言来进行开发
去年Arvato Systems 聘请我带领团队构建 Ch它是一种新平台用于将非赢利性团体和捐赠人组织在一起与很多 Internet 公司一样我们向客户展示了可购买的实际产品与其他公司不同的是这些 产品 指的是提供机会比如一名癌症研究员一小时服务收费 美元帮助盲人收费 美元或者保护一英亩雨林一个月收费 美元我们面临两大挑战一份很紧凑的时间表和长期的复杂性
开发工作从九月份开始要求必须在十一月份之前建立起一个站点以便有机会赶上圣诞节期间的通信量高峰(最终我们超出了十一月份这个期限两星期)在其他开发语言中基于 Java 的解决方案可能要花费 到 个月才能完成这一任务因此生产力是一个很重要的考虑因素这与 Java 部署思想相悖
通过对竞争对手和项目的考察我们了解到我们希望能够每天给站点带来几百万次点击的通信量而我们需要每天有几十万次成功的点击因此可伸缩性也是一个考虑因素这与 Java 部署思想相符
最后我们了解到发布初始站点只是一个开始我们只实现了总体规划的百分之三因此我们所选的技术需要根据复杂性和负载作出一些调整我认为 Ruby 语言在复杂性方面会更易于调整因为它提供了对更高级语言和特性(如开放类)的支持以及具有更少配置需求和更简单集成化编程模型的 Rails 框架
虽然我们面临着时间和可伸缩性方面的挑战但是也拥有一些其他的有利因素我们拥有一张完全空白的候选名单可以选择想要的任何技术任何团队可以定义项目培训和全部的技术我们可完全自由地作出选择
Java 语言是一种优秀的通用语言它总是应用于新的技术领域如嵌入式系统和移动设备Java 语言在广泛关注的集成方面也表现优秀它具有高性能流行并受到平台的良好支持但是正如您在本系列中所了解到的那样Java 语言并不是用于数据库支持的 Web 应用程序的 greenfield 开发的最佳选择
相比之下Ruby on Rails 框架则是新的并没有很多人使用 Rails 开发高通信流量的站点并且几乎不存在什么使用 Rails 多年开发项目的经验但它仍然是一种高生产力的数据库支持的 Web 应用程序开发的框架最后尽管我们对 Rails 缺乏开发长期项目的经验和并未得到广泛部署有所顾虑但那份紧凑的时间表驱使我们选择了 Ruby on Rails
作出这个决定之后我们发现招募项目人才非常容易我们还发现早期的生产力优势非常显着 —— 甚至比预期的还要好我们确实遇到了一些早期的稳定性问题因此加强了测试工作的力度此后稳定性得到了极大的改进
原理
每个框架设计者都使用一组假定来构造该框架的重写原理学习遵守该原理的约束可使您愉快地进行编程而挑战这些约束则会使您的编程受挫Rails 框架和 Java 框架拥有很多不同的原理
Rails 是一种集成框架需要使用高度利用 Ruby 语言的动态本质Rails 开发人员强调框架的生产力特性而不是工具特性并且常常将 Web 架构看得非常简单在本系列的前几篇文章中您已了解到这一点Java 设计者通常必须分块地组合开发环境独立地选择持久性Web 和集成层他们通常严重地依赖工具来简化核心任务Web 架构设计趋向于较为复杂
完全集成
Java 框架往往是解决一个小问题(比如持久性或查看组织)而 Rails 则是一个集成环境Rails 开发人员的优势在于不必解决与许多不同框架集成的问题大多数 Hibernate 开发人员陷入了过早关闭与 Java Web 框架之间的连接的陷阱Rails 视图框架是从头构建的以便与 ActiveRecord 集成(Rails 持久性框架)当您考察用于 Web 服务配置和插件的 Rails 框架时也会发现类似的经验Java 编程支持各种不同的框架对于所有这些框架使用不同的集成策略
Java 开发人员的优势在于选择如果您需要从头构建一个框架则可能要考虑使用基于 SQL 的解决方案用于数据库集成(如 iBATIS 或 Java 编程中基于 JDBC 的包装框架之一)反过来如果要使用一种古老的模式进行编程则可能要使用对象关系映射框架(如 Hibernate)相比之下如果您使用 Rails则拥有一个主要选择ActiveRecord这意味着 Java 框架提供了更多的选择有时能提供更好的集成开发项目的解决方案但是由于我们要开发一个 greenfield 项目因此选择算不上是一个问题
一种动态语言
Rails 原理的下一个主要部分是动态编程语言Java 工具往往可以有效地使用 Java 类型模型提供的额外信息工具可以识别错误和有效地重构代码Rails 还可有效地利用编程语言的优点Ruby 是一种构建特定于域的语言(DSL)的理想语言Rails 集中使用 DSL 来完成从构建模型对象之间的关系到指定自定义组件(如状态机器或可上传的图像)的所有工作动态语言常常更加简洁因此 Rails 项目比 Java 项目要简练得多可让用户更简练地表达代码和配置在 Ch 项目中我们发现技术顶尖的程序员可达到更高的生产力但是我们确实需要招募经验更丰富的开发人员我对这种妥协非常满意
传统的 Java 程序员对 IDE 有着近乎虔诚的热爱造成这一现象有充分的理由IDE 提供了语法的完整性检查修正了小错误并提供了增量编译以便更快地完成编码编译部署和测试这样的周期最近几年来开发环境开始更好地利用编译循环和静态类型提供的信息IDE 现在编辑抽象语法树(AST)而不是(或者同时)编辑代码的文本表示这一策略允许使用强大的代码重构工具而使用静态类型语言的同样方法来实现此功能则困难得多
静态类型确实能更好地使用工具但是也存在缺点强制使用静态类型通常需要编译器而编译步骤必然会降低生产力使用 Rails我可以更改一行代码并重新加载浏览器就可立即看到更改的结果与 Java 开发人员相比大多数 Ruby 开发人员只使用一种很好的编辑器TextMate 是最流行的 Ruby on Rails 编辑器它提供了语法突出显示代码完整性检查以及一些频繁使用的结构的良好的模板支持而当发现可将所有简单的基于 Ruby 的脚本(用作基本的 Rails 工具包)放入编辑器中时您会更加喜出望外与纯粹的调试器不同的是我可以使用断点脚本该脚本可停止特定的应用程序进入一个 Ruby 解释程序我可在其中调用方法检查变量的值以及甚至在恢复执行之间修改代码
简单的架构
传统的 Web 端 Java 架构包括一个用于域对象和数据访问对象的层一个提供业务级 API 的外观层一个控制器层和一个视图层此架构比典型的 模型视图控制器 架构(使用 Smalltalk 语言最早创建)稍微复杂一些相比之下Ruby on Rails 包括一个使用 ActiveRecord 设计模式的模型层一个控制器层和一个视图层我们喜欢易于获得的 Rails 方法它更加简练并且带来额外的复杂性和错误的机会更小
惯例优先原则
Java 框架通常可以自由地使用 XML 配置而 Rails 主要使用惯例来避免可能的配置在程序员必须指定配置的位置Rails 通常依赖 Ruby(常常以 DSL 形式)来提供配置对于 greenfield 开发我发现惯例优先于配置是很有意义的该策略为我省去了很多行代码更简化了必须编写的代码估计我们所需的配置只有传统 Java 应用程序中所指定的十分之一我们有时会损失一点灵活性但这并不足以抵消使用此策略带来的节省
总而言之Rails 框架的原理适合解决 Ch 项目中的问题集成的各种工具让我可以利用框架实现更多的功能而无需自己进行过多的集成惯例优先原则 为我节省了配置站点的时间动态语言为经验丰富的开发人员提供了更多的能力和灵活性同时也使他们能够利用更少的代码表达更强大的思想该框架适合于我们团队的能力和要解决的业务问题
持久性
Java 和 Ruby 语言的最流行的持久性框架可以比任何其他特性更好地阐明 Java 和 Ruby 经验之间的区别Java 开发人员通常使用 Hibernate它是一种对象关系映射框架通过 Hibernate您可获取现有的模型和模式并使用注释或 XML 表达二者之间的映射Hibernate 类是简单传统 Java 对象(POJO)它的每个对象派生自一个通用的基类大多数配置是显式的使用注释XML 或二者的某种结合
而 ActiveRecord 是一种包装的框架就是说每个类都是现有类的包装器ActiveRecord 根据关联表的内容(如表中每列的一个属性)自动地向模型对象添加特性所有的类都从一个通用的基类继承ActiveRecord 主要利用通用约定来推断配置例如
ActiveRecord 利用类名的复数形式来推出表名
主键的名称为 id
列表的排序顺序由 position
字段决定
对象关系映射是使用遗留模式(可能定义时没有考虑对象模型)时的最佳解决方案但是当您能为应用程序显式地设计数据库模式时您通常不需要映射框架了我们将 ActiveRecord 看作我们的一个巨大优点我们可以包含关系数据库需要时转入 SQL 并在适当的时候退出
迁移
Rails 迁移使我们能够用代码表示模式的两个版本之间的差别和它们所包含的数据之间的差别对每个迁移都进行了命名和编号可在任何时候恢复到任何版本迁移有以下一些确切的优点
产生错误代码时可恢复到一个旧版本的模式
用代码而不是 SQL 来表达模式更便于我们使用
在最大程度上与数据库独立
但是迁移也有一些限制如果两个开发人员同时创建迁移则编号会出现混乱所以我们必须手动处理我们通过有效的通信来使这些问题最小化团队成员构建需使用迁移的新模型时发出通知但是这个模型依赖于团队的开发人员较少或迁移进展较慢的情况
ActiveRecord 还有其他的限制其中一些是故意作出的Rails 的创建者认为数据库的约束和组成应归入应用程序而不是数据库这种思想带来了一些副作用ActiveRecord 使用视图的情况不是很好构建过程(克隆模式复制测试数据并运行测试)并不能正确地进行复制ActiveRecord 在使用参考完整性约束的某些场合也会出现问题因为某些类型的关联可能连接到多个数据库表跨越复杂模型进行预先加载很复杂通常在连接多行时需要使用 SQL继承也受到限制使用 ActiveRecord 时我被迫使用单表继承 映射策略而该策略并不总是最佳选择
所有的持久性策略都充满了妥协我认为 ActiveRecord 实现了一组有效的妥协常常选择了简单性总而言之ActiveRecord 和迁移是我们的积极推动我们可以快速地构建解决方案我们拥有足够的 SQL 访问权可在需要时改进系统性能但是当 ActiveRecord 并不总能应对挑战时最好将 Rails 应用于使用老旧模式的项目一些替代的持久性模型正在出现包括 RBatis一种 iBATIS Java 框架的端口现在讨论 RBatis 的有效性还为时过早
结束语
对于我的团队和项目来说Ruby on Rails 被证明相当有效我还不知道这个项目的最终规模如何因为撰写本文时该系统才运行 个月现在只是开始增加通信量但是我们对生产力却很了解我知道团队的预算比竞争公司(这些公司常常使用 Java 解决方案)的要低得多我对我们的生产力也很有信心
通过跨越边界 系列我向您介绍了 Java 领域以外的语言和解决方案但程序员毕竟是技术人员每个高明的技术人员的工具包中都应包含适用于每个解决方案的广泛的工具集除工具外本系列中介绍的观点也为您展示了一些其他思路现在一些框架设计者甚至将 SeasideRails 中的技术甚至 JavaScript 应用于 Java 框架中找机会进行同样的应用继续 跨越边界
关于作者
Bruce Tate 是位父亲山地车手皮艇手住在德克萨斯州的奥斯汀他是三本最畅销 Java 图书的作者包括获得 Jolt 奖的 Better Faster Lighter Java他最近推出了 From Java to Ruby 和 Rails: Up and Running他在 IBM 工作了 年而后创立了 RapidRed 顾问公司在那里他专攻基于 Ruby 的轻量级开发策略和体系结构及 Ruby on Rails 框架现在他是 WellGood LLC 的 CTO 该公司致力于为非盈利组织和福利机构开辟市场