紀念我的J2EE啓蒙項目(轉)

孔子說:“學而不思則罔,思而不學則殆。”從我做項目的經歷,我深深感嘆古人的智慧。

去年我畢業設計時候做的一個項目是畢業設計管理系統。當時老師給我們的要求是用Jsp 和 Javabean來實現。我主要負責項目建模和Javabean部分,後來負責數據庫的同學被SARS困在家不能返校,所以數據庫部分也由我來完成了。當時對java的知識是非常貧乏的,只有簡單的語法基礎,連jsp、 javabean都是第一次聽到過。不知道從哪裏開始,於是就研究老師給的一個非常簡單的演示項目,首先覺得javabean是個比較簡單的東西,於是就先開始分析用戶需求。

需求怎麼來描述呢?由於學校教育的侷限,加上自己研究了很長時間高數,政治,英語(幹什麼用大家都明白吧),造成了知識的嚴重匱乏。我就以參加畢業設計人員爲中心,建立他們的需求列表。但是這樣的需求描述是非常片面的,幾乎都是靜態的需求,而且很難設計幾個用戶的交互過程,和某些狀態的改變。而且這種需求是否有真正符合需要是個問題,所以我立刻和同學一起設計web界面,目的很簡單,因爲用戶的需求都是通過界面來實現的,對界面的設計能涵蓋最終提交用戶的功能,而且還能發掘很多非用戶的需求。這樣我在實現這些功能的時候會有數。在一年後當我仔細看過用例分析,uml後感覺是如獲至寶。心頭的結全部打開。

建模該怎麼建?當時真是什麼概念都沒有,雖然知道uml,但是僅僅知道皮毛。於是我就很簡單的從對象的角度考慮問題,首先找畢業設計管理系統中涉及的人員,這個是很明顯找出來的,涉及到學生,教師,管理員,進一步提取出項目對象,各個人員之間通訊的消息對象,畢業設計文檔對象,等等。接下來設計數據庫表,由於學校教數據庫的時候只有講述了ER圖到關係模式的轉化,而根本沒有講ODL到關係模式的轉化(現在看來,ODL應該更好,對理解O/R Mapping有更多的幫助。)於是就畫ER圖,建立各種表,設定主鍵,外鍵,等等。

我遇到的首要問題就是學生,教師,管理員該怎麼辦,每一個對象建一個表,似乎也沒有什麼問題。但是在建立消息對象的時候就有問題了,因爲消息是要在學生,教師,管理員任意兩者都能傳送的,我建消息表的時候是這樣設計的:一個消息的內容,消息的發送人,消息的接收人(消息內容和發送人一個表,爲解決冗餘接收人單獨一個表),這樣這個消息就有很大的靈活程度了,消息發送人可以是三種角色間的隨便一種,更進一步,這樣的消息可以是系統生成的消息,而且接收者可以是隨便哪種角色。但隨之帶來的問題是:消息發送人、接收者該去參照誰呢?顯然填一個發送者、接收者的id是不夠的,必須連發送者的角色也填上。第二種方法,每個角色有一個收到消息的列表,讓它去參照單一的消息表id,這樣也能解決問題。第三種方法,把學生,教師,管理員合併成一個用戶表,那麼消息發送人,接收人只要簡單的參照一個通用的用戶id了。這種合併子類的方法會造成教師,管理員的行有些null值,但是考慮到教師和管理員的數目遠遠小於學生的數目,所以這樣的冗餘是忽略不計的。最後我還是選擇了最後一種方法。不過我還是覺的是對象模型向關係模型的一種妥協。

接着我着手設計javabean,訪問數據庫用的是單純的JDBC。因爲我對前臺界面要訪問一個對象的哪些屬性是不瞭解的,事實上也不該瞭解。所以我只能從數據庫取出一個完整的對象,讓他來決定究竟要訪問哪些屬性。而且對對象的修改也是這樣,前臺修改好新的對象的屬性,我來進行更數據庫的同步。於是我的javabean就出現了這樣的方法:load()、 store()、 update()、delete()這樣的對象和數據庫表同步的方法。這在一年後的今天看來,實際上我已經自己做了一個O/R Mapping的工作了。當時自己做這樣的O/R Mapping是相當痛苦的事情,而且用的方法也是最粗淺的,就是整個對象更數據庫同步,即使沒有修改的屬性也同步,所以要同步的話,必須要先把對象從數據庫取出來,修改後再寫回。而且要求所有屬性必須是nullable的non-primitive類型。第二個問題,對錶中的外鍵怎麼反應到bean中?比如每個學生有一個輔導教師,在數據庫中很明顯學生表需要一個外鍵參照教師id,但是在StudentBean中教師屬性是寫Teacher對象呢,還是Teacher id值呢,按照面向對象,很明顯應該Teacher對象,但是我就覺得這是個多麼heavy的事情呀!再想想教師情況,一個教師有一個Collection的Student對象是多麼“重”!意味着load一個教師要load所有的students。於是我採用的方法是:還是用對象,比如teacher 的學生屬性,還是一個collection  Student對象,同時提供了一個loadStudents()的方法,load teacher對象的時候並不load 學生屬性,只有bean的用戶顯式調用loadStudents()的時候纔會加載一組student對象,這在現在看來似乎我自覺不自覺的實現了lazy loading?

現在審視當時編寫的javabean,犯的最大最大的錯誤就是把data access object和business workflow混在一起了。比如把教師所有要調用的功能都放在教師對象裏面,而有些功能有時是要涉及幾個bean的。現在看了簡直是慘不忍睹的設計,雖然當時也困惑過,但是卻沒有動動腦筋來解決,我現在看了session bean 的思路時,覺得是那麼的舒暢、自然。

當時困惑的還有關於jsp的處理,jsp只是負責顯示的,但是爲什麼一個jsp提交的數據要給另外一個jsp去處理呢?這是很不舒暢的做法,於是有了最初的servlet做控制器的想法,但是當時顧不了研究那麼多,也沒有最終實現,畢竟前臺不是我負責的。當我現在知道了Structs,MVC Model2的時候,我以前的困惑都隨之解決。又一次感覺非常舒暢。

以上是我一個新手的第一個項目的一些情況,是在非常閉塞的環境裏面做的,上網都是電話卡撥號,根本沒有接觸到主流技術,但是正是這種環境下積累的無數困惑,使我遇到EJB、Hibernate、Structs的時候如飲甘露,迅速的吸收了他們的營養。否則我想學習這些技術無疑是很耗時間的,因爲根本沒有更這種技術產生共鳴,不知道他們搞那麼多框框究竟想幹什麼,而我產生了一種共鳴,他們這些框框,正是開我心頭疑惑的鎖。

又回到開頭孔子的話,事實上我在做項目中,一直都是按自己的思考方式在“思”而沒有去“學”,所以常常感到疲倦而無所得,即“殆”。但是如果不實際自己去動手做,而光光學j2ee,必然會很迷惑,即“罔”。我感覺先有困惑再有解決,是掌握技術的十分有效的而且鞏固的方法,而且很有創造性,是屬於歸納的思考方式。所以碰到問題,首先需要想想按常理該怎麼去解決,再去尋找別人怎麼解決的,這樣自己提高會十分迅速。

現在我正在用EJB、Structs來重寫那個項目。雖然我對整個需求已經相當清楚,畢竟有了第一個項目作爲原型,但是我還是試圖使用比較規範的方法來設計。首先分析了很多use case,並且使用Rational的RequisitePro來跟蹤需求的變化。接着又使用Rose對use case畫了use case diagram。對關鍵對象交互過程畫了 sequence diagram,對某些對象的狀態畫了state diagram。最後很自然的導出了最重要的class diagram。

我現在最大的困惑在Entity Bean上面,建模的時候很自然會有一個User對象,Student, Teacher, Manager 對象繼承自這個User對象。那數據庫表怎麼設計呢?第一種,三個對象三個表,對應StudentBean TeacherBean ManagerBean三個EntityBean。第二種,三個對象一個表,只有一個UserBean。第三種,把User單獨一個對象,那麼就是四個對象四個表,這樣一個子對象,要映射兩張表EntityBean不行吧。
暫時不管究竟用什麼設計,來考察一下文檔對像DocBean,我們主要看看它的cmr部分,一個cmr是author 屬性,它應該是三種角色對象中的一種,問題來了,如果三個對象三個表,那麼我這裏的cmr怎麼寫?
public abstract StudentLocal  getAuthor(); ?
public abstract TeacherLocal  getAuthor(); ?
public abstract ManagerLocal  getAuthor(); ?
考察第二種,三個對象一個表,只有一個UserBean。那這個是相當簡單的只需要
public abstract UserLocal getAuthor();
那最後只有第二種方式才能解決問題,就是隻有一個User對象,於是我爲了使用EntityBean不得不對我的模型進行修改,把原來清晰的三個對象揉合到一起,雖然說以後某個student變成了teacher 或者manager可以很方便的升級,但是這種情況在我這個例子裏面是很少有的,而且數據庫這種合併子類的方法給teacher,manager形成了很多的null值,雖然這是忽略不計的,但是總不是優雅的做法,最關鍵的是我的對象模型又一次向關係模型妥協了。

CMR的另外一個問題是,要CMR必須把所有相關的Bean放一個包裏面,在一個xml文件裏面配置,我的項目裏面的entity bean都是要更user有關係的,那我都需要把這些bean打成一個包,用一個xml描述!我的還是小項目,如果一個項目有100個entity bean,這些entity bean都是相互關聯的,那我要把這100個entity bean放在打成一個包,用一個xml配置!那這個xml文件該有多長!一個小小的錯誤全部完蛋。

看來Entity Bean確實如很多人說的那樣不是十分靈活,我也開始傾向於用hibernate來做持久化了,但是我需要有足夠的靈活性,我想繼承和多態的支持還是很重要的,這樣才能真正是“object-oriented domain models”方式,而不是以數據庫表爲中心的方式,引用Hibernate的一句話“Hibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the Java-based middle-tier”

我對AOP也是十分關注,因爲它實在是太激動人心的概念了,有了AOP那麼我們還要容器幹什麼,容器的功能完全可以通過AOP來實現,就像Spring框架,除了沒有分佈式外幾乎都能支持吧。而jboss4.0也已經通過AOP實現了。考察我的項目,我發現有一個這樣的需求需要滿足,就是:在執行某些business logic之前必須要檢測對這個方法的調用是否超過期限了,比如學生去選擇研究課題,如果選擇過了就不能再次選擇,雖然這個方法可以由前臺很簡單的解決,但是我覺得在業務邏輯層防止這種行爲的發生是業務完整性的一個部分。而通過AOP,把這樣一個很多方法都要調用的公共功能作爲一個aspect是很好的。我的問題是在容器中運行的EJB能夠用AOP嗎?在Jboss4.0裏面各種EJB都是通過AOP方式來提供服務的,似乎我自己多加一層業務層面的服務應該是可行的(使用jboss aop),但是這樣的EJB放在別的容器裏面運行會怎麼樣?會影響到容器對EJB的干預嗎?請各位前輩指點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章