用J2ee體系架構(gòu)開發(fā)應(yīng)用程序容易嗎,?如果你覺得是,那么就一個最簡單的應(yīng)用:對一個表進行插入,、刪除,、修改和列表的界面,和Delphi,、VB,、PB比較一下——當然,這里純粹就開發(fā)效率而言——JSP/Servlet+EJB的模式和前者相差不止一個數(shù)量級,。即使是一個新手用Delphi也可以在5分鐘之內(nèi)完成這個任務(wù),,用同樣的時間恐怕連deploy descriptor都創(chuàng)建不完。J2EE支持者可能會聲稱:業(yè)務(wù)邏輯集中在中間層的EJB中,,更加有利于今后代碼的維護,,同時可以在以后的項目中復(fù)用;可以實現(xiàn)數(shù)據(jù)庫無關(guān),,減少數(shù)據(jù)平臺遷移的代價,,等等,。果真是這樣嗎?
數(shù)據(jù)在存儲層是關(guān)系模型,,而在中間層是對象模型,;關(guān)系型的數(shù)據(jù)結(jié)構(gòu)的訪問,、處理是通過SQL來完成的,,而對象的訪問則是通過方法調(diào)用、參數(shù)傳遞來完成的,。這之間的巨大鴻溝——O/R mapping,,以及EJB處理O/R mapping時的偽對象方式,是直接導(dǎo)致J2EE體系架構(gòu)開發(fā)效率低下的主要原因,。1.x以前的EJB模式中,,每個Entity bean都有一堆get/set方法,以及相關(guān)的findByxxx方法——這是面向?qū)ο蟮姆绞絾??get/set與直接訪問成員變量有何區(qū)別呢,?同時,書寫,、維護這樣的代碼簡直就是噩夢,,任何字段的增減都會同時造成多處代碼( Business interface, remote interface, entity bean object)的改動。有鑒于此,,后來J2EE開發(fā)者中提出了Rowset passing, value object, Hashtable attribute access等多種所謂J2ee設(shè)計模式來解決這個問題,,本質(zhì)上還是回到了數(shù)據(jù)訪問的非對象方式。同時也出現(xiàn)了castor,CocoBase等多種O/R mapping工具,,通過自動生成代碼的方式減少重復(fù)勞動,。即便是用這些工具自動維護所有的get/set,由于表現(xiàn)層(JSP/servlet)與應(yīng)用層之間還是通過對象訪問的方式來進行,,每次get/set方法的增減,、參數(shù)改變都會影響到相應(yīng)的表現(xiàn)層代碼,而表現(xiàn)層中對EJB的訪問代碼是O/R工具無法維護的,。 J2EE體系結(jié)構(gòu)的設(shè)計初衷之一是為了分離應(yīng)用邏輯和數(shù)據(jù)存儲層,,同時分離表現(xiàn)層和應(yīng)用邏輯層。然而這兩個目的一個都無法達到,。首先為了讓業(yè)務(wù)邏輯獨立于數(shù)據(jù)存儲結(jié)構(gòu),,就必須建立數(shù)據(jù)在業(yè)務(wù)邏輯層的對象形式;而反觀entity bean的get/set方法,,entity bean和session bean以及session bean和JSP/servlet之間的rowset/hashtable參數(shù)傳遞方式,,這種所謂的entity對象與面向?qū)ο蠛翢o關(guān)系,是徹頭徹尾的面向過程式的方法,。將參數(shù)放進一個散列表然后傳給entity bean的insert方法,,這和直接調(diào)用insert into xxx有什么本質(zhì)區(qū)別呢,?EJB2.0規(guī)范試圖在deploy descriptor中用對象關(guān)系描述的方式解決O/R問題,這不過也是對實際關(guān)系存儲模型的簡單照搬,。相比起sql語言的簡潔,、高效,將關(guān)系模型的數(shù)據(jù)轉(zhuǎn)變成EJB再通過方法調(diào)用的方式來訪問數(shù)據(jù)就顯得笨拙,、臃腫,。Entity bean這種偽對象只不過是關(guān)系型數(shù)據(jù)在內(nèi)存中的一份強類型的復(fù)制,不但沒有擺脫與數(shù)據(jù)存儲層的緊密耦合,,反而還因為額外的O/R轉(zhuǎn)換工作,,增添了多余的負擔。其次,,即使通過分離接口和實際的實現(xiàn)存儲的代碼來建立抽象的對象模型,,實際應(yīng)用中由于需求變化頻繁,能夠僅僅改變實現(xiàn)而不變動接口就滿足需求變更的情況是很少的,。據(jù)個例子,,以前中客戶的地址是保存在兩個字段address1,address2,而后來由于業(yè)務(wù)需要將地址做成一個單獨的表,,和客戶之間是多對一的關(guān)系,,那么客戶對象的更新、插入接口就必須由以前的兩個String參數(shù),,變成一個Collection類型的參數(shù),。可見,,目前的EJB對象在隔離存儲模型方面是多么脆弱,。鼓吹J2EE的人對J2EE程序能夠不做任何改動就從MySQL遷移到Oracle或者從Linux遷移到Solaris的特性津津樂道,然而相比起數(shù)據(jù)庫平臺的變動,,更加頻繁,、代價更大的數(shù)據(jù)存儲模型的變動,業(yè)務(wù)邏輯的變動,,J2EE和C/S一樣無能為力,,甚至代價更大。而對于表現(xiàn)層,,JSP/Servlet同樣是通過預(yù)先約定的接口來訪問中間層對象,。一旦接口發(fā)生任何改變,如參數(shù)個數(shù),、數(shù)據(jù)類型發(fā)生變化,,表現(xiàn)層的代碼就必須有所改動。由于在EJB模型下中間層無法很好地分離業(yè)務(wù)接口與實現(xiàn),,表現(xiàn)層對需求變動所要付出變動的代價同樣是高昂的,。同樣由于中間層業(yè)務(wù)邏輯對象與數(shù)據(jù)存儲層的緊密耦合,,要能在其他項目中重用這些對象幾乎是不可能的。訂單處理是許多項目經(jīng)常要完成的模塊,,有誰能直接從市場上買一套訂單處理的EJB直接應(yīng)用到項目中嗎,?或者將以前瓷磚行業(yè)MRPII項目中的訂單對象直接應(yīng)用于PC制造業(yè)?對于J2EE體系在開發(fā)和維護上造成的麻煩,,sun J2EE藍圖中的pet store示例程序是最典型的例子了,。設(shè)計者為Entity bean引入了所謂的data access層來實現(xiàn)entity bean與數(shù)據(jù)庫的無關(guān)性,但是看看那些超長的insert方法的參數(shù)列表吧——實際項目不是演示完就萬事大吉了,,如果要增加刪除一些字段怎么辦,?entity bean和DAO都得修改,,這種代碼的維護代價實在是奇高無比,。 最近微軟用.Net重寫了pet store程序,并用它和Websphere版的pet store進行了對比(參見MSDN),。無論是性能上還是代碼行數(shù),,.Net版的pet sotre都大大優(yōu)于J2EE版。有意思的是.Net版實際上是用stored procedure來完成業(yè)務(wù)邏輯的,,也就是說它和純粹JSP/servlet+database的體系結(jié)構(gòu)是等同的,。請不要激動,為什么不呢,?性能上EJB絕對無法同stored procedure相比,;可分布性方面SQL server 2000和IIS能直接應(yīng)用win2000的COM+組件的負載均衡特性——這可是操作系統(tǒng)級的分布式應(yīng)用,TPS的世界紀錄就是SQL server 2000在win2000集群上實現(xiàn)的,;開發(fā)和維護代價方面,,如前所述,在同等文檔水平上EJB恐怕還比不上stored procedure,。J2EE剩下的唯一的長處就是不同數(shù)據(jù)庫和操作系統(tǒng)平臺之間的遷移了,,然而這真的很重要嗎? 不可否認,,J2EE體系架構(gòu)是一個重大的突破,,但降低開發(fā)維護代價和軟件復(fù)用絕對不是J2EE的長處。在這方面還需要強大的開發(fā)工具,、建模工具和CASE環(huán)境來支持,,這正是目前J2EE非常欠缺的。 |
|