本文发表在 rolia.net 枫下论坛标题 J2EE的最大谎言(转)
降低开发维护代价——J2EE的最大谎言
用J2ee体系架构开发应用程序容易吗?如果你觉得是,那么就一个最简单的应用:对一个表进行插入、删除、修改和列表的界面,和Delphi、VB、PB比较一下——当然,这里纯粹就开发效率而言——JSP/Servlet+EJB的模式和前者相差不止一个数量级。即使是一个新手用Delphi也可以在5分钟之内完成这个任务,用同样的时间恐怕连deploy descriptor都创建不完。J2EE支持者可能会声称:业务逻辑集中在中间层的EJB中,更加有利于今后代码的维护,同时可以在以后的项目中复用;可以实现数据库无关,减少数据平台迁移的代价,等等。果真是这样吗?
数据在存储层是关系模型,而在中间层是对象模型;关系型的数据结构的访问、处理是通过SQL来完成的,而对象的访问则是通过方法调用、参数传递来完成的。这之间的巨大鸿沟——O/R mapping,以及EJB处理O/R mapping时的伪对象方式,是直接导致J2EE体系架构开发效率低下的主要原因。1.x以前的EJB模式中,每个Entity bean都有一堆get/set方法,以及相关的findByxxx方法——这是面向对象的方式吗?get/set与直接访问成员变量有何区别呢?同时,书写、维护这样的代码简直就是噩梦,任何字段的增减都会同时造成多处代码( Business interface, remote interface, entity bean object)的改动。有鉴于此,后来J2EE开发者中提出了Rowset passing, value object, Hashtable attribute access等多种所谓J2ee设计模式来解决这个问题,本质上还是回到了数据访问的非对象方式。同时也出现了castor,CocoBase等多种O/R mapping工具,通过自动生成代码的方式减少重复劳动。即便是用这些工具自动维护所有的get/set,由于表现层(JSP/servlet)与应用层之间还是通过对象访问的方式来进行,每次get/set方法的增减、参数改变都会影响到相应的表现层代码,而表现层中对EJB的访问代码是O/R工具无法维护的。
J2EE体系结构的设计初衷之一是为了分离应用逻辑和数据存储层,同时分离表现层和应用逻辑层。然而这两个目的一个都无法达到。首先为了让业务逻辑独立于数据存储结构,就必须建立数据在业务逻辑层的对象形式;而反观entity bean的get/set方法,entity bean和session bean以及session bean和JSP/servlet之间的rowset/hashtable参数传递方式,这种所谓的entity对象与面向对象毫无关系,是彻头彻尾的面向过程式的方法。将参数放进一个散列表然后传给entity bean的insert方法,这和直接调用insert into xxx有什么本质区别呢?EJB2.0规范试图在deploy descriptor中用对象关系描述的方式解决O/R问题,这不过也是对实际关系存储模型的简单照搬。相比起sql语言的简洁、高效,将关系模型的数据转变成EJB再通过方法调用的方式来访问数据就显得笨拙、臃肿。Entity bean这种伪对象只不过是关系型数据在内存中的一份强类型的复制,不但没有摆脱与数据存储层的紧密耦合,反而还因为额外的O/R转换工作,增添了多余的负担。其次,即使通过分离接口和实际的实现存储的代码来建立抽象的对象模型,实际应用中由于需求变化频繁,能够仅仅改变实现而不变动接口就满足需求变更的情况是很少的。据个例子,以前中客户的地址是保存在两个字段address1,address2,而后来由于业务需要将地址做成一个单独的表,和客户之间是多对一的关系,那么客户对象的更新、插入接口就必须由以前的两个String参数,变成一个Collection类型的参数。可见,目前的EJB对象在隔离存储模型方面是多么脆弱。鼓吹J2EE的人对J2EE程序能够不做任何改动就从MySQL迁移到Oracle或者从Linux迁移到Solaris的特性津津乐道,然而相比起数据库平台的变动,更加频繁、代价更大的数据存储模型的变动,业务逻辑的变动,J2EE和C/S一样无能为力,甚至代价更大。而对于表现层,JSP/Servlet同样是通过预先约定的接口来访问中间层对象。一旦接口发生任何改变,如参数个数、数据类型发生变化,表现层的代码就必须有所改动。由于在EJB模型下中间层无法很好地分离业务接口与实现,表现层对需求变动所要付出变动的代价同样是高昂的。同样由于中间层业务逻辑对象与数据存储层的紧密耦合,要能在其他项目中重用这些对象几乎是不可能的。订单处理是许多项目经常要完成的模块,有谁能直接从市场上买一套订单处理的EJB直接应用到项目中吗?或者将以前瓷砖行业MRPII项目中的订单对象直接应用于PC制造业?对于J2EE体系在开发和维护上造成的麻烦,sun J2EE蓝图中的pet store示例程序是最典型的例子了。设计者为Entity bean引入了所谓的data access层来实现entity bean与数据库的无关性,但是看看那些超长的insert方法的参数列表吧——实际项目不是演示完就万事大吉了,如果要增加删除一些字段怎么办?entity bean和DAO都得修改,这种代码的维护代价实在是奇高无比。
最近微软用.Net重写了pet store程序,并用它和Websphere版的pet store进行了对比(参见MSDN)。无论是性能上还是代码行数,.Net版的pet sotre都大大优于J2EE版。有意思的是.Net版实际上是用stored procedure来完成业务逻辑的,也就是说它和纯粹JSP/servlet+database的体系结构是等同的。请不要激动,为什么不呢?性能上EJB绝对无法同stored procedure相比;可分布性方面SQL server 2000和IIS能直接应用win2000的COM+组件的负载均衡特性——这可是操作系统级的分布式应用,TPS的世界纪录就是SQL server 2000在win2000集群上实现的;开发和维护代价方面,如前所述,在同等文档水平上EJB恐怕还比不上stored procedure。J2EE剩下的唯一的长处就是不同数据库和操作系统平台之间的迁移了,然而这真的很重要吗?
不可否认,J2EE体系架构是一个重大的突破,但降低开发维护代价和软件复用绝对不是J2EE的长处。在这方面还需要强大的开发工具、建模工具和CASE环境来支持,这正是目前J2EE非常欠缺的。更多精彩文章及讨论,请光临枫下论坛 rolia.net
降低开发维护代价——J2EE的最大谎言
用J2ee体系架构开发应用程序容易吗?如果你觉得是,那么就一个最简单的应用:对一个表进行插入、删除、修改和列表的界面,和Delphi、VB、PB比较一下——当然,这里纯粹就开发效率而言——JSP/Servlet+EJB的模式和前者相差不止一个数量级。即使是一个新手用Delphi也可以在5分钟之内完成这个任务,用同样的时间恐怕连deploy descriptor都创建不完。J2EE支持者可能会声称:业务逻辑集中在中间层的EJB中,更加有利于今后代码的维护,同时可以在以后的项目中复用;可以实现数据库无关,减少数据平台迁移的代价,等等。果真是这样吗?
数据在存储层是关系模型,而在中间层是对象模型;关系型的数据结构的访问、处理是通过SQL来完成的,而对象的访问则是通过方法调用、参数传递来完成的。这之间的巨大鸿沟——O/R mapping,以及EJB处理O/R mapping时的伪对象方式,是直接导致J2EE体系架构开发效率低下的主要原因。1.x以前的EJB模式中,每个Entity bean都有一堆get/set方法,以及相关的findByxxx方法——这是面向对象的方式吗?get/set与直接访问成员变量有何区别呢?同时,书写、维护这样的代码简直就是噩梦,任何字段的增减都会同时造成多处代码( Business interface, remote interface, entity bean object)的改动。有鉴于此,后来J2EE开发者中提出了Rowset passing, value object, Hashtable attribute access等多种所谓J2ee设计模式来解决这个问题,本质上还是回到了数据访问的非对象方式。同时也出现了castor,CocoBase等多种O/R mapping工具,通过自动生成代码的方式减少重复劳动。即便是用这些工具自动维护所有的get/set,由于表现层(JSP/servlet)与应用层之间还是通过对象访问的方式来进行,每次get/set方法的增减、参数改变都会影响到相应的表现层代码,而表现层中对EJB的访问代码是O/R工具无法维护的。
J2EE体系结构的设计初衷之一是为了分离应用逻辑和数据存储层,同时分离表现层和应用逻辑层。然而这两个目的一个都无法达到。首先为了让业务逻辑独立于数据存储结构,就必须建立数据在业务逻辑层的对象形式;而反观entity bean的get/set方法,entity bean和session bean以及session bean和JSP/servlet之间的rowset/hashtable参数传递方式,这种所谓的entity对象与面向对象毫无关系,是彻头彻尾的面向过程式的方法。将参数放进一个散列表然后传给entity bean的insert方法,这和直接调用insert into xxx有什么本质区别呢?EJB2.0规范试图在deploy descriptor中用对象关系描述的方式解决O/R问题,这不过也是对实际关系存储模型的简单照搬。相比起sql语言的简洁、高效,将关系模型的数据转变成EJB再通过方法调用的方式来访问数据就显得笨拙、臃肿。Entity bean这种伪对象只不过是关系型数据在内存中的一份强类型的复制,不但没有摆脱与数据存储层的紧密耦合,反而还因为额外的O/R转换工作,增添了多余的负担。其次,即使通过分离接口和实际的实现存储的代码来建立抽象的对象模型,实际应用中由于需求变化频繁,能够仅仅改变实现而不变动接口就满足需求变更的情况是很少的。据个例子,以前中客户的地址是保存在两个字段address1,address2,而后来由于业务需要将地址做成一个单独的表,和客户之间是多对一的关系,那么客户对象的更新、插入接口就必须由以前的两个String参数,变成一个Collection类型的参数。可见,目前的EJB对象在隔离存储模型方面是多么脆弱。鼓吹J2EE的人对J2EE程序能够不做任何改动就从MySQL迁移到Oracle或者从Linux迁移到Solaris的特性津津乐道,然而相比起数据库平台的变动,更加频繁、代价更大的数据存储模型的变动,业务逻辑的变动,J2EE和C/S一样无能为力,甚至代价更大。而对于表现层,JSP/Servlet同样是通过预先约定的接口来访问中间层对象。一旦接口发生任何改变,如参数个数、数据类型发生变化,表现层的代码就必须有所改动。由于在EJB模型下中间层无法很好地分离业务接口与实现,表现层对需求变动所要付出变动的代价同样是高昂的。同样由于中间层业务逻辑对象与数据存储层的紧密耦合,要能在其他项目中重用这些对象几乎是不可能的。订单处理是许多项目经常要完成的模块,有谁能直接从市场上买一套订单处理的EJB直接应用到项目中吗?或者将以前瓷砖行业MRPII项目中的订单对象直接应用于PC制造业?对于J2EE体系在开发和维护上造成的麻烦,sun J2EE蓝图中的pet store示例程序是最典型的例子了。设计者为Entity bean引入了所谓的data access层来实现entity bean与数据库的无关性,但是看看那些超长的insert方法的参数列表吧——实际项目不是演示完就万事大吉了,如果要增加删除一些字段怎么办?entity bean和DAO都得修改,这种代码的维护代价实在是奇高无比。
最近微软用.Net重写了pet store程序,并用它和Websphere版的pet store进行了对比(参见MSDN)。无论是性能上还是代码行数,.Net版的pet sotre都大大优于J2EE版。有意思的是.Net版实际上是用stored procedure来完成业务逻辑的,也就是说它和纯粹JSP/servlet+database的体系结构是等同的。请不要激动,为什么不呢?性能上EJB绝对无法同stored procedure相比;可分布性方面SQL server 2000和IIS能直接应用win2000的COM+组件的负载均衡特性——这可是操作系统级的分布式应用,TPS的世界纪录就是SQL server 2000在win2000集群上实现的;开发和维护代价方面,如前所述,在同等文档水平上EJB恐怕还比不上stored procedure。J2EE剩下的唯一的长处就是不同数据库和操作系统平台之间的迁移了,然而这真的很重要吗?
不可否认,J2EE体系架构是一个重大的突破,但降低开发维护代价和软件复用绝对不是J2EE的长处。在这方面还需要强大的开发工具、建模工具和CASE环境来支持,这正是目前J2EE非常欠缺的。更多精彩文章及讨论,请光临枫下论坛 rolia.net