孔子說:“學(xué)而不思則罔,思而不學(xué)則殆。”從我做項目的經(jīng)歷,我深深感嘆古人的智慧。
去年我畢業(yè)設(shè)計時候做的一個項目是畢業(yè)設(shè)計管理系統(tǒng)。當(dāng)時老師給我們的要求是用Jsp 和 Javabean來實現(xiàn)。我主要負(fù)責(zé)項目建模和Javabean部分,后來負(fù)責(zé)數(shù)據(jù)庫的同學(xué)被SARS困在家不能返校,所以數(shù)據(jù)庫部分也由我來完成了。當(dāng)時對java的知識是非常貧乏的,只有簡單的語法基礎(chǔ),連jsp、 javabean都是第一次聽到過。不知道從哪里開始,于是就研究老師給的一個非常簡單的演示項目,首先覺得javabean是個比較簡單的東西,于是就先開始分析用戶需求。
需求怎么來描述呢?由于學(xué)校教育的局限,加上自己研究了很長時間高數(shù),政治,英語(干什么用大家都明白吧),造成了知識的嚴(yán)重匱乏。我就以參加畢業(yè)設(shè)計人員為中心,建立他們的需求列表。但是這樣的需求描述是非常片面的,幾乎都是靜態(tài)的需求,而且很難設(shè)計幾個用戶的交互過程,和某些狀態(tài)的改變。而且這種需求是否有真正符合需要是個問題,所以我立刻和同學(xué)一起設(shè)計web界面,目的很簡單,因為用戶的需求都是通過界面來實現(xiàn)的,對界面的設(shè)計能涵蓋最終提交用戶的功能,而且還能發(fā)掘很多非用戶的需求。這樣我在實現(xiàn)這些功能的時候會有數(shù)。在一年后當(dāng)我仔細(xì)看過用例分析,uml后感覺是如獲至寶。心頭的結(jié)全部打開。
建模該怎么建?當(dāng)時真是什么概念都沒有,雖然知道uml,但是僅僅知道皮毛。于是我就很簡單的從對象的角度考慮問題,首先找畢業(yè)設(shè)計管理系統(tǒng)中涉及的人員,這個是很明顯找出來的,涉及到學(xué)生,教師,管理員,進(jìn)一步提取出項目對象,各個人員之間通訊的消息對象,畢業(yè)設(shè)計文檔對象,等等。接下來設(shè)計數(shù)據(jù)庫表,由于學(xué)校教數(shù)據(jù)庫的時候只有講述了ER圖到關(guān)系模式的轉(zhuǎn)化,而根本沒有講ODL到關(guān)系模式的轉(zhuǎn)化(現(xiàn)在看來,ODL應(yīng)該更好,對理解O/R Mapping有更多的幫助。)于是就畫ER圖,建立各種表,設(shè)定主鍵,外鍵,等等。
我遇到的首要問題就是學(xué)生,教師,管理員該怎么辦,每一個對象建一個表,似乎也沒有什么問題。但是在建立消息對象的時候就有問題了,因為消息是要在學(xué)生,教師,管理員任意兩者都能傳送的,我建消息表的時候是這樣設(shè)計的:一個消息的內(nèi)容,消息的發(fā)送人,消息的接收人(消息內(nèi)容和發(fā)送人一個表,為解決冗余接收人單獨一個表),這樣這個消息就有很大的靈活程度了,消息發(fā)送人可以是三種角色間的隨便一種,更進(jìn)一步,這樣的消息可以是系統(tǒng)生成的消息,而且接收者可以是隨便哪種角色。但隨之帶來的問題是:消息發(fā)送人、接收者該去參照誰呢?顯然填一個發(fā)送者、接收者的id是不夠的,必須連發(fā)送者的角色也填上。第二種方法,每個角色有一個收到消息的列表,讓它去參照單一的消息表id,這樣也能解決問題。第三種方法,把學(xué)生,教師,管理員合并成一個用戶表,那么消息發(fā)送人,接收人只要簡單的參照一個通用的用戶id了。這種合并子類的方法會造成教師,管理員的行有些null值,但是考慮到教師和管理員的數(shù)目遠(yuǎn)遠(yuǎn)小于學(xué)生的數(shù)目,所以這樣的冗余是忽略不計的。最后我還是選擇了最后一種方法。不過我還是覺的是對象模型向關(guān)系模型的一種妥協(xié)。
接著我著手設(shè)計javabean,訪問數(shù)據(jù)庫用的是單純的JDBC。因為我對前臺界面要訪問一個對象的哪些屬性是不了解的,事實上也不該了解。所以我只能從數(shù)據(jù)庫取出一個完整的對象,讓他來決定究竟要訪問哪些屬性。而且對對象的修改也是這樣,前臺修改好新的對象的屬性,我來進(jìn)行更數(shù)據(jù)庫的同步。于是我的javabean就出現(xiàn)了這樣的方法:load()、 store()、 update()、delete()這樣的對象和數(shù)據(jù)庫表同步的方法。這在一年后的今天看來,實際上我已經(jīng)自己做了一個O/R Mapping的工作了。當(dāng)時自己做這樣的O/R Mapping是相當(dāng)痛苦的事情,而且用的方法也是最粗淺的,就是整個對象更數(shù)據(jù)庫同步,即使沒有修改的屬性也同步,所以要同步的話,必須要先把對象從數(shù)據(jù)庫取出來,修改后再寫回。而且要求所有屬性必須是nullable的non-primitive類型。第二個問題,對表中的外鍵怎么反應(yīng)到bean中?比如每個學(xué)生有一個輔導(dǎo)教師,在數(shù)據(jù)庫中很明顯學(xué)生表需要一個外鍵參照教師id,但是在StudentBean中教師屬性是寫Teacher對象呢,還是Teacher id值呢,按照面向?qū)ο螅苊黠@應(yīng)該Teacher對象,但是我就覺得這是個多么heavy的事情呀!再想想教師情況,一個教師有一個Collection的Student對象是多么“重”!意味著load一個教師要load所有的students。于是我采用的方法是:還是用對象,比如teacher 的學(xué)生屬性,還是一個collection Student對象,同時提供了一個loadStudents()的方法,load teacher對象的時候并不load 學(xué)生屬性,只有bean的用戶顯式調(diào)用loadStudents()的時候才會加載一組student對象,這在現(xiàn)在看來似乎我自覺不自覺的實現(xiàn)了lazy loading?
現(xiàn)在審視當(dāng)時編寫的javabean,犯的最大最大的錯誤就是把data access object和business workflow混在一起了。比如把教師所有要調(diào)用的功能都放在教師對象里面,而有些功能有時是要涉及幾個bean的。現(xiàn)在看了簡直是慘不忍睹的設(shè)計,雖然當(dāng)時也困惑過,但是卻沒有動動腦筋來解決,我現(xiàn)在看了session bean 的思路時,覺得是那么的舒暢、自然。
當(dāng)時困惑的還有關(guān)于jsp的處理,jsp只是負(fù)責(zé)顯示的,但是為什么一個jsp提交的數(shù)據(jù)要給另外一個jsp去處理呢?這是很不舒暢的做法,于是有了最初的servlet做控制器的想法,但是當(dāng)時顧不了研究那么多,也沒有最終實現(xiàn),畢竟前臺不是我負(fù)責(zé)的。當(dāng)我現(xiàn)在知道了Structs,MVC Model2的時候,我以前的困惑都隨之解決。又一次感覺非常舒暢。
以上是我一個新手的第一個項目的一些情況,是在非常閉塞的環(huán)境里面做的,上網(wǎng)都是電話卡撥號,根本沒有接觸到主流技術(shù),但是正是這種環(huán)境下積累的無數(shù)困惑,使我遇到EJB、Hibernate、Structs的時候如飲甘露,迅速的吸收了他們的營養(yǎng)。否則我想學(xué)習(xí)這些技術(shù)無疑是很耗時間的,因為根本沒有更這種技術(shù)產(chǎn)生共鳴,不知道他們搞那么多框框究竟想干什么,而我產(chǎn)生了一種共鳴,他們這些框框,正是開我心頭疑惑的鎖。
又回到開頭孔子的話,事實上我在做項目中,一直都是按自己的思考方式在“思”而沒有去“學(xué)”,所以常常感到疲倦而無所得,即“殆”。但是如果不實際自己去動手做,而光光學(xué)j2ee,必然會很迷惑,即“罔”。我感覺先有困惑再有解決,是掌握技術(shù)的十分有效的而且鞏固的方法,而且很有創(chuàng)造性,是屬于歸納的思考方式。所以碰到問題,首先需要想想按常理該怎么去解決,再去尋找別人怎么解決的,這樣自己提高會十分迅速。
現(xiàn)在我正在用EJB、Structs來重寫那個項目。雖然我對整個需求已經(jīng)相當(dāng)清楚,畢竟有了第一個項目作為原型,但是我還是試圖使用比較規(guī)范的方法來設(shè)計。首先分析了很多use case,并且使用Rational的RequisitePro來跟蹤需求的變化。接著又使用Rose對use case畫了use case diagram。對關(guān)鍵對象交互過程畫了 sequence diagram,對某些對象的狀態(tài)畫了state diagram。最后很自然的導(dǎo)出了最重要的class diagram。
我現(xiàn)在最大的困惑在Entity Bean上面,建模的時候很自然會有一個User對象,Student, Teacher, Manager 對象繼承自這個User對象。那數(shù)據(jù)庫表怎么設(shè)計呢?第一種,三個對象三個表,對應(yīng)StudentBean TeacherBean ManagerBean三個EntityBean。第二種,三個對象一個表,只有一個UserBean。第三種,把User單獨一個對象,那么就是四個對象四個表,這樣一個子對象,要映射兩張表EntityBean不行吧。
暫時不管究竟用什么設(shè)計,來考察一下文檔對像DocBean,我們主要看看它的cmr部分,一個cmr是author 屬性,它應(yīng)該是三種角色對象中的一種,問題來了,如果三個對象三個表,那么我這里的cmr怎么寫?
public abstract StudentLocal getAuthor(); ?
public abstract TeacherLocal getAuthor(); ?
public abstract ManagerLocal getAuthor(); ?
考察第二種,三個對象一個表,只有一個UserBean。那這個是相當(dāng)簡單的只需要
public abstract UserLocal getAuthor();
那最后只有第二種方式才能解決問題,就是只有一個User對象,于是我為了使用EntityBean不得不對我的模型進(jìn)行修改,把原來清晰的三個對象揉合到一起,雖然說以后某個student變成了teacher 或者manager可以很方便的升級,但是這種情況在我這個例子里面是很少有的,而且數(shù)據(jù)庫這種合并子類的方法給teacher,manager形成了很多的null值,雖然這是忽略不計的,但是總不是優(yōu)雅的做法,最關(guān)鍵的是我的對象模型又一次向關(guān)系模型妥協(xié)了。
CMR的另外一個問題是,要CMR必須把所有相關(guān)的Bean放一個包里面,在一個xml文件里面配置,我的項目里面的entity bean都是要更user有關(guān)系的,那我都需要把這些bean打成一個包,用一個xml描述!我的還是小項目,如果一個項目有100個entity bean,這些entity bean都是相互關(guān)聯(lián)的,那我要把這100個entity bean放在打成一個包,用一個xml配置!那這個xml文件該有多長!一個小小的錯誤全部完蛋。
看來Entity Bean確實如很多人說的那樣不是十分靈活,我也開始傾向于用hibernate來做持久化了,但是我需要有足夠的靈活性,我想繼承和多態(tài)的支持還是很重要的,這樣才能真正是“object-oriented domain models”方式,而不是以數(shù)據(jù)庫表為中心的方式,引用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也是十分關(guān)注,因為它實在是太激動人心的概念了,有了AOP那么我們還要容器干什么,容器的功能完全可以通過AOP來實現(xiàn),就像Spring框架,除了沒有分布式外幾乎都能支持吧。而jboss4.0也已經(jīng)通過AOP實現(xiàn)了。考察我的項目,我發(fā)現(xiàn)有一個這樣的需求需要滿足,就是:在執(zhí)行某些business logic之前必須要檢測對這個方法的調(diào)用是否超過期限了,比如學(xué)生去選擇研究課題,如果選擇過了就不能再次選擇,雖然這個方法可以由前臺很簡單的解決,但是我覺得在業(yè)務(wù)邏輯層防止這種行為的發(fā)生是業(yè)務(wù)完整性的一個部分。而通過AOP,把這樣一個很多方法都要調(diào)用的公共功能作為一個aspect是很好的。我的問題是在容器中運(yùn)行的EJB能夠用AOP嗎?在Jboss4.0里面各種EJB都是通過AOP方式來提供服務(wù)的,似乎我自己多加一層業(yè)務(wù)層面的服務(wù)應(yīng)該是可行的(使用jboss aop),但是這樣的EJB放在別的容器里面運(yùn)行會怎么樣?會影響到容器對EJB的干預(yù)嗎?請各位前輩指點。
posted on 2005-10-27 12:48
rkind 閱讀(153)
評論(0) 編輯 收藏 所屬分類:
學(xué)習(xí)方法與經(jīng)驗