Ruby On Rails 与Jdon Framework架构比较

板桥里人 http://www.jdon.com 2006/04/21

  本文试图比较同属快速开发性质的Ruby on Rails(以下简称RoR)和Jdon Framework(以下简称JF)在架构上异同,供大家在实际架构选择中比较。

  RoR 是一个使用Ruby语言写就的Web应用框架,Ruby语言是类似Python, Smalltalk, PHP和Perl的动态类型语言。从新特点层面看,Ruby on Rails并没有提供比其他已经存在的Web应用框架新的东西,它的唯一特点就是快速开发。RoR大概诞生于2004年6月份。

  JF是使用Java语言编写的、基于Ioc/AOP微容器的快速开发工具。JF是基于JdonSD构件库增删改查框架基础上发展起来的,1.0版本是在2004年12月底完成。当时推出时很难定位,时至今日,它应该是Ruby on Rails、Spring和JBoss容器三个概念的一个中间体。属于域驱动开发框架(DDDD:omain Driven Development framework )。

  JF虽是笔者操作完成,其实它是国人网络结晶的开源产物,很多需求和思想都来自Jdon社区热情参与者,看到很多初学者总是为简单和灵活做痛苦选择,为了写一个简单系统,要么走入Jsp+JavaBeans误区,要么被复杂技术配置缠身,忘记业务本职工作。JF推出后,道友提出各种建议甚至猛烈批判,这些都形成了JF发展动力,促进JF完善。从用户出发的简易之道使JF的起点和立意一下和RoR这样国外产品走到了一起,下面我们详细进行一下两者架构比较。

语言之争

  RoR代表的是动态类型语言派别;而Java是一种静态类型语言,当初Java刚刚诞生时,这种两种类型派别之争曾经发生在Java和Smalltalk之间,后来现实选择了Java这样静态类型语言,关键原因时动态类型语言在编程时难以找出其一些潜在的语言Bug。

  但是,随着软件工程中单元测试的重视,动态类型语言+单元测试的结合克服了以上缺憾,从而使得RoR这样得动态类型语言重新东山再起。

  促成RoR受到敏捷工程派别的领域专家(如Martin Fowler)推崇另外一个原因是,它被认为是一种domain-specific languages(DSL)实现,DSL是一种专门供领域建模专家(也就是系统分析师)使用的语言,这些领域专家不同于程序高手,他们有一套自己认知世界和表达世界的思维和方式(如UML),因此,他们不感兴趣于软件设计细节,希望软件能够按照他们分析设计的结果去运行和执行就可以了。

  其实,DSL并不是一个全新理念,它已经在我们软件领域中反复出现,例如:我们很多人是关系数据库领域专家,所以,尽管由O/R Mapping这样工具出现,但是并没有改变这些领域专家表达方式,他们还是使用SQL等严谨的数学思维来实现他们的表达方式。

  DSL概念非常好,但是是否有必要重新搞一套DSL语言则涉及很多方面的问题,新的语言总会在实践中有新的陷阱,Java经过十多年发展,成熟和发展是其特点。

  当然,别以为RoR顶着DSL新名词就是一个非常好的东西,对其本质微词已经不绝于耳,有人认为它实质不过就是1994的Visual FoxPro(Ruby on Rails is a Bloody Square Turd ),提出该观点的作者认为: 为什么我们在一个没有重构以及调试支持的编码环境中工作?为什么还要重覆以前的痛苦呢?如果你确实喜欢RoR的ActiveRecord,为什么不用.NET呢?RoR 不是开发Web应用平台, RoR is a cult(RoR是宗教崇拜,笔者注:大概因为是因为所谓大师级的人推荐原因).

  无论如何,让我们抛开争执,通过比较看看RoR一些特点。

多层架构

  现在多层架构已经深入人心,多层主要是指表现层MVC、业务层和持久层多层分离的体系,由于Java是一个技术自由选择的世界,因此,每个层面都有不同的具体框架技术供选择, 提供选择是一种好事,但是又可能是一种坏事,需要应用者花费精力学习和研究,这非常类似我们购物。

  在微软世界,由于各层框架技术几乎都是由一家提供的,所以,.NET就索性将这些框架直接集成 到IDE开发工具,对于有的程序员感觉.NET用起来很快,将开发工具和框架混合成一体,但这是以绑定为代价的,甚至有程序员反感Java世界IDE+框架的开发方式,认为在开发工具之外还会多一个框架,并且认为违背KISS(keep it simple and stupid)原则,其实不然,关键是追求KISS原则的同时,不要使自己受制于某个厂商或平台,使自己变得简单以及愚蠢
(失去自己作为客户的上帝位置,被厂商忽视,成为简单而愚蠢的人),此为KMSS(keep me simple and stupid)。

  在Java世界,多层结构实现路径很多,从MVC到持久层框架有各种选择,例如我们可以使用Struts以及Hibernate组合成一个J2EE多层 应用系统。

  Rails也提供了model/view/controller多层实现,但是各层的实现框架也确定下来,省却了程序员在多个框架之间选择带来的“麻烦”(这是相对的)。

  而Jdon Framework则类似RoR这种提供缺省各层实现设计,表现层在struts基础上进行了CRUD流程抽象;通过提供JdbcTemp实现持久层技术,当然,持久层具体实现也可以选择hibernate等其他框架实现,秉承提供缺省的,但是也是可替换的宗旨。

  下图是两者各层架构比较图:

ror

  我们通过上图可以看出,两者流程基本一致,所不同的主要是两点:RoR的Action Pack和Active Record,下面我们就这两点解释如下:

Action Pack

  Action Pack是RoR的MVC组件框架:

  View templates,相当于Struts中的Jsp;

  URL routing,相当于struts-config.xml流程配置,RoR不是使用XML配置,而是作为脚本代码,这也是一些人吹嘘的RoR无繁多XML配置真相所在,其实,XML也是一种脚本,从某种意义上来说:XML比语言脚本更简单易写(至少语法不多)。

  ActionController,初看相当于struts的DispatchAction,但是因为其包含业务逻辑,而我们在java中是不推荐在在控制层action中写业务逻辑的。

  ActionController功能在于:RoR可以将浏览器的请求直接映射到ActionController类的方法上,这有些类似Struts中的DispatchAction,但是,在Java中,业务逻辑是不推荐写在表现层的控制类中的,控制类只是负责前后台流程协调,是一种Mediator模式实现,不应该让其加入更多职责;在JF中,业务逻辑是写在Service类中,JF通过自己的命令服务调用模式,也可以直接将浏览器的请求直接映射到Service类的方法上,例如,调用http://localhost//MyWeb/abc.do?method=xxx,将直接激活Service类的xxx方法,程序员直接编写xxx方法内容即可。

  RoR的Filters过滤器也是Action Pack的一个部分,主要用来实现一些通用功能的动态插入,这在JF中是通过AOP拦截器和Decorator模式来实现的,见AOP vs Decorator 一文,在JiveJdon3.0中,我们通过拦截器实现了组件方法权限访问、以及缓存等通用功能。

  Action Pack中还有Helpers功能相当于Struts的标签库,它可以把Model/ActionForm和Action以及html连接在一起,下面是RoR的Helpers如:

<form action="save_person" method="post">
  Name: <%= text_field "person", "name", "size" => 20 %>
  ....
</form>

  当然,RoR的Helpers有很多种类,如Form Helpers/javascriptHelpers等等,相当于一个个API 库。它的Ajax & JavaScript helpers倒是很时髦的。

  RoR的Layouts相当于Struts的Tiles,这就不多说了。

  Scaffolding提供一个数据表的CRUD功能实现,Scaffolding类似代码生成器,生成CRUD代码,缺点是,如果你更改了代码,Scaffolding会在覆盖,必须再更改Scaffolding设置,实际用起来比较麻烦。而JF的CRUD是一个MVC流程上的精简,属于开发框架性质,而不是代码生成,只需要通过jdonframework.xml配置:

<model key="messageId" class="com.jdon.jivejdon.model.ForumMessage">
  <actionForm name="messageForm"/>
  <handler>
    <service ref="forumMessageService">
      <initMethod name="initMessage"/>
      <getMethod name="findMessage"/>
      <createMethod name="createTopicMessage"/>
      <updateMethod name="updateMessage"/>
      <deleteMethod name="deleteMessage"/>
    </service>
  </handler>
</model>

Active Record

  RoR有一个Active Record组件,其实就是ORM实现,相当于Hibernate,它是Martin Fowler的 Active Record pattern实现,它是指一个既包含数据又包含行为的对象,这些数据需要持久保存到对应的数据表中。Active Record一个很明显的特征是:将数据访问逻辑也包含在这个domain对象中,通过这种办法让人们可以知道如何从数据库读写数据。如下图:

active Record

  Active Record其实类似JF中Domain Object + Dao,也就是将Dao中对数据库的CRUD方法和Domain Object整合在一起, 我们知道,Dao模式本质是桥模式,通过Dao可以将不同的数据库访问实现分离,并且在运行时组合,但是,Martin Fowler将Dao从Domain Object分离出去的对象称为贫血对象。

  他的这个观点笔者认为不是从技术观点,而是从领域建模角度出发的,其实从技术观点讲,将Dao从Domain Object中分离在设计上非常灵活,例如使用JF开发的JiveJdon3.0中,我们就可以在Dao层中使用Decorator模式(过滤器)加入一层缓存,这样,虽然我们Dao层使用的SQL实现,我们也是可以实现持久层缓存,JiveJdon3.0整个Dao层设计相当于一个Hibernate的微型(或者说轻量化),好处是:JiveJdon3.0这样实现的缓存可以被各层如表现层直接访问,减少路径,提升运行性能。

RoR秘籍

  通过以上分析,我们也许已经明白RoR和JF定位,大家为什么突然拥戴RoR,这是因为大家比较厌恶XML配置,太复杂的XML配置只能增加开发的复杂性,而JF则在这方面从以下几个方面进行了努力:

  1. 表现层的struts-config.xml配置是模板化的,CRUD流程固化模板化,拷贝粘贴就可以完成配置。
  2. JF自身的配置很简单,只包括两个部分Models和Services,配置语法主要集中再Models部分,语法数量不超过10个,Models负责CRUD流程配置,如果你不需要使用CRUD可以不用配置;Services是业务类配置,对于一个POJO类,只要写:<pojoService class="POJO类名" name="随便取个名" />如下:
  3. <pojoService name="forumService"
           class="com.jdon.jivejdon.service.imp.ForumServiceImp"/>

    <pojoService name="forumMessageService"
           class="com.jdon.jivejdon.service.imp.ForumMessageShell"/>

    至于,这些POJO之间的调用关系,无需指定,这由JF内置IOC微容器自动配对解决。
  4. 持久层试图通过JdbcTemp或SQL语句简化配置,当然Hibernate等java工具也在进步,不断重构,相信会越来越简单。

  总结:相比RoR,作为DDD(Domain Driven Development framework )实现的JF在快速开发和软件高质量上作了一个平衡,相当于在RoR(快速,但非主流语言)和Spring(高质量,但配置烦琐)之间做了一个平衡。

  JF一直也试图争取获得国外软件专家的认可,可能会因为其他因素未能如愿,但是,作为和RoR几乎同时诞生的国产JF,作为由国人网民共同参与的结晶,已经用事实证明,国人有能力靠创新冲刺世界软件领域的前列。

  无论如何,在RoR精神的召引下,Java世界将引来第四代语言4GL时代,同时能够满足求简单或求灵活等不同编程心理的需求,迎来新的发展。


参考文章:
What Is Ruby on Rails
What are Domain Specific Languages?
Ruby on Rails is a Bloody Square Turd

在Ruby on Rails/Naked Objects精神指引下的域驱动开发框架

Java项目开发中常见根本性认识误区

领域模型驱动设计(DDD)之模型提炼

DSM:Domain-Specific Modeling

发表讨论