做一個(gè)好的架構(gòu)師一直是我的一個(gè)目標(biāo)。但是,做過(guò)這么多項(xiàng)目后發(fā)現(xiàn)自己在設(shè)計(jì)上仍存在很多不足。下文是一篇不錯(cuò)的文章。希望大家能夠得到一些啟示或共鳴。歡迎大家發(fā)表自己在架構(gòu)設(shè)計(jì)方面的觀點(diǎn)。
我們期待自己成為一個(gè)優(yōu)秀的軟件模型設(shè)計(jì)者,但是,要怎樣做,又從哪里開始呢?將下列原則應(yīng)用到你的軟件工程中,你會(huì)獲得立桿見影的成果。
1. 人遠(yuǎn)比技術(shù)重要 你開發(fā)軟件是為了供別人使用,沒(méi)有人使用的軟件只是沒(méi)有意義的數(shù)據(jù)的集合而已。許多在軟件方面很有成就的行家在他們事業(yè)的初期卻表現(xiàn)平平,因?yàn)樗麄兡菚r(shí)侯將主要精力都集中在技術(shù)上。顯然,構(gòu)件(components),EJB(Enterprise Java Beans)和代理(agent)是很有趣的東西。但是對(duì)于用戶來(lái)說(shuō),如果你設(shè)計(jì)的軟件很難使用或者不能滿足他們的需求,后臺(tái)用再好的技術(shù)也于事無(wú)補(bǔ)。多花點(diǎn)時(shí)間到軟件需求和設(shè)計(jì)一個(gè)使用戶能很容易理解的界面上。
2. 理解你要實(shí)現(xiàn)的東西 好的軟件設(shè)計(jì)人員把大多數(shù)時(shí)間花費(fèi)在建立系統(tǒng)模型上,偶爾寫一些源代碼,但那只不過(guò)是為了驗(yàn)證設(shè)計(jì)過(guò)程中所遇到的問(wèn)題。這將使他們的設(shè)計(jì)方案更加可行。
3. 謙虛是必須的品格 你不可能知道一切,你甚至要很努力才能獲得足夠用的知識(shí)。軟件開發(fā)是一項(xiàng)復(fù)雜而艱巨的工作,因?yàn)檐浖_發(fā)所用到的工具和技術(shù)是在不斷更新的。而且,一個(gè)人也不可能了解軟件開發(fā)的所有過(guò)程。在日常生活中你每天接觸到的新鮮事物可能不會(huì)太多。但是對(duì)于從事軟件開發(fā)的人來(lái)說(shuō),每天可以學(xué)習(xí)很多新東西(如果愿意的話)。
4. 需求就是需求 如果你沒(méi)有任何需求,你就不要?jiǎng)邮珠_發(fā)任何軟件。成功的軟件取決于時(shí)間(在用戶要求的時(shí)間內(nèi)完成)、預(yù)算和是否滿足用戶的需求。如果你不能確切知道用戶需要的是什么,或者軟件的需求定義,那么你的工程注定會(huì)失敗。
5. 需求其實(shí)很少改變,改變的是你對(duì)需求的理解 Object ToolSmiths公司(www.objecttoolsmiths.com)的Doug Smith常喜歡說(shuō):“分析是一門科學(xué),設(shè)計(jì)是一門藝術(shù)”。他的意思是說(shuō)在眾多的“正確”分析模型中只存在一個(gè)最“正確”分析模型可以完全滿足解決某個(gè)具體問(wèn)題的需要(我理解的意思是需求分析需要一絲不茍、精確的完成,而設(shè)計(jì)的時(shí)候反而可以發(fā)揮創(chuàng)造力和想象力 - 譯者注)。
如果需求經(jīng)常改動(dòng),很可能是你沒(méi)有作好需求分析,并不是需求真的改變了。
你可以抱怨用戶不能告訴你他們想得到什么,但是不要忘記,收集需求信息是你工作。
你可以說(shuō)是新來(lái)的開發(fā)人員把事情搞得一團(tuán)糟,但是,你應(yīng)該確定在工程的第一天就告訴他們應(yīng)該做什么和怎樣去做。 如果你覺得公司不讓你與用戶充分接觸,那只能說(shuō)明公司的管理層并不是真正支持你的項(xiàng)目。
你可以抱怨公司有關(guān)軟件工程的管理制度不合理,但你必須了解大多同行公司是怎么做的。
你可以借口說(shuō)你們的競(jìng)爭(zhēng)對(duì)手的成功是因?yàn)樗麄冇辛艘粋€(gè)新的理念,但是為什么你沒(méi)先想到呢?
需求真正改變的情況很少,但是沒(méi)有做好需求分析工作的理由卻很多。
6. 經(jīng)常閱讀 在這個(gè)每日都在發(fā)生變化的產(chǎn)業(yè)中,你不可能在已取得的成就上陶醉太久。
每個(gè)月至少讀2、3本專業(yè)雜志或者1本專業(yè)書籍。保持不落伍需要付出很多的時(shí)間和金錢,但會(huì)使你成為一個(gè)很有實(shí)力的競(jìng)爭(zhēng)者。
7. 降低軟件模塊間的耦合度 高耦合度的系統(tǒng)是很難維護(hù)的。一處的修改引起另一處甚至更多處的變動(dòng)。
你可以通過(guò)以下方法降低程序的耦合度:隱藏實(shí)現(xiàn)細(xì)節(jié),強(qiáng)制構(gòu)件接口定義,不使用公用數(shù)據(jù)結(jié)構(gòu),不讓應(yīng)用程序直接操作數(shù)據(jù)庫(kù)(我的經(jīng)驗(yàn)法則是:當(dāng)應(yīng)用程序員在寫SQL代碼的時(shí)候,你的程序的耦合度就已經(jīng)很高了)。
耦合度低的軟件可以很容易被重用、維護(hù)和擴(kuò)充。
8. 提高軟件的內(nèi)聚性 如果一個(gè)軟件的模塊只實(shí)現(xiàn)一個(gè)功能,那么該模塊具有高內(nèi)聚性。高內(nèi)聚性的軟件更容易維護(hù)和改進(jìn)。
判斷一個(gè)模塊是否有高的內(nèi)聚性,看一看你是否能夠用一個(gè)簡(jiǎn)單的句子描述它的功能就行了。如果你用了一段話或者你需要使用類似“和”、“或”等連詞,則說(shuō)明你需要將該模塊細(xì)化。
只有高內(nèi)聚性的模塊才可能被重用。
9. 考慮軟件的移植性 移植是軟件開發(fā)中一項(xiàng)具體而又實(shí)際的工作,不要相信某些軟件工具的廣告宣傳(比如java 的宣傳口號(hào)write once run many 譯者注)。
即使僅僅對(duì)軟件進(jìn)行常規(guī)升級(jí),也要把這看得和向另一個(gè)操作系統(tǒng)或數(shù)據(jù)庫(kù)移植一樣重要。
記得從16位Windows移植到32位windows的“樂(lè)趣”嗎 ?當(dāng)你使用了某個(gè)操作系統(tǒng)的特性,如它的進(jìn)程間通信(IPC)策略,或用某數(shù)據(jù)庫(kù)專有語(yǔ)言寫了存儲(chǔ)過(guò)程。你的軟件和那個(gè)特定的產(chǎn)品結(jié)合度就已經(jīng)很高了。
好的軟件設(shè)計(jì)者把那些特有的實(shí)現(xiàn)細(xì)節(jié)打包隱藏起來(lái),所以,當(dāng)那些特性該變的時(shí)候,你的僅僅需要更新那個(gè)包就可以了。
10. 接受變化 這是一句老話了:惟一不變的只有變化。
你應(yīng)該將所有系統(tǒng)將可能發(fā)生的變化以及潛在需求記錄下來(lái),以便將來(lái)能夠?qū)崿F(xiàn)。 通過(guò)在建模期間考慮這些假設(shè)的情況,你就有可能開發(fā)出足夠強(qiáng)壯且容易維護(hù)的軟件。設(shè)計(jì)強(qiáng)壯的軟件是你最基本的目標(biāo)。
11. 不要低估對(duì)軟件規(guī)模的需求 Internet 帶給我們的最大的教訓(xùn)是你必須在軟件開發(fā)的最初階段就考慮軟件規(guī)模的可擴(kuò)充性。
今天只有100人的部門使用的應(yīng)用程序,明天可能會(huì)被有好幾萬(wàn)人的組織使用,下月,通過(guò)因特網(wǎng)可能會(huì)有幾百萬(wàn)人使用它。
在軟件設(shè)計(jì)的初期,根據(jù)在用例模型中定義的必須支持的基本事務(wù)處理,確定軟件的基本功能。然后,在建造系統(tǒng)的時(shí)候再逐步加入比較常用的功能。
在設(shè)計(jì)的開始考慮軟件的規(guī)模需求,避免在用戶群突然增大的情況下,重寫軟件。
12. 性能僅僅是很多設(shè)計(jì)因素之一 關(guān)注軟件設(shè)計(jì)中的一個(gè)重要因素--性能,這好象也是用戶最關(guān)心的事情。一個(gè)性能不佳的軟件將不可避免被重寫。
但是你的設(shè)計(jì)還必須具有可靠性,可用性,便攜性和可擴(kuò)展性。你應(yīng)該在工程開始就應(yīng)該定義并區(qū)分好這些因素,以便在工作中恰當(dāng)使用。性能可以是,也可以不是優(yōu)先級(jí)最高的因素,我的觀點(diǎn)是,給每個(gè)設(shè)計(jì)因素應(yīng)有的考慮。
13. 管理接口 “UML User Guide”(Grady Booch,Ivar Jacobson和Jim Rumbaugh ,Addison Wesley, 1999)中指出,你應(yīng)該在開發(fā)階段的早期就定義軟件模塊之間的接口。
這有助于你的開發(fā)人員全面理解軟件的設(shè)計(jì)結(jié)構(gòu)并取得一致意見,讓各模塊開發(fā)小組相對(duì)獨(dú)立的工作。一旦模塊的接口確定之后,模塊怎樣實(shí)現(xiàn)就不是很重要了。
從根本上說(shuō),如果你不能夠定義你的模塊“從外部看上去會(huì)是什么樣子”,你肯定也不清楚模塊內(nèi)要實(shí)現(xiàn)什么。
14. 走近路需要更長(zhǎng)的時(shí)間 在軟件開發(fā)中沒(méi)有捷徑可以走。 縮短你的在需求分析上花的時(shí)間,結(jié)果只能是開發(fā)出來(lái)的軟件不能滿足用戶的需求,必須被重寫。
在軟件建模上每節(jié)省一周,在將來(lái)的編碼階段可能會(huì)多花幾周時(shí)間,因?yàn)槟阍谌嫠伎贾熬蛣?dòng)手寫程序。
你為了節(jié)省一天的測(cè)試時(shí)間而漏掉了一個(gè)bug,在將來(lái)的維護(hù)階段,可能需要花幾周甚至幾個(gè)月的時(shí)間去修復(fù)。與其如此,還不如重新安排一下項(xiàng)目計(jì)劃。
避免走捷徑,只做一次但要做對(duì)(do it once by doing it right)。
15. 別信賴任何人 產(chǎn)品和服務(wù)銷售公司不是你的朋友,你的大部分員工和高層管理人員也不是。
大部分產(chǎn)品供應(yīng)商希望把你牢牢綁在他們的產(chǎn)品上,可能是操作系統(tǒng),數(shù)據(jù)庫(kù)或者某個(gè)開發(fā)工具。
大部分的顧問(wèn)和承包商只關(guān)心你的錢并不是你的工程(停止向他們付款,看一看他們會(huì)在周圍呆多長(zhǎng)時(shí)間)。
大部分程序員認(rèn)為他們自己比其他人更優(yōu)秀,他們可能拋棄你設(shè)計(jì)的模型而用自己認(rèn)為更好的。
只有良好的溝通才能解決這些問(wèn)題。
要明確的是,不要只依靠一家產(chǎn)品或服務(wù)提供商,即使你的公司(或組織)已經(jīng)在建模、文檔和過(guò)程等方面向那個(gè)公司投入了很多錢。
16. 證明你的設(shè)計(jì)在實(shí)踐中可行 在設(shè)計(jì)的時(shí)候應(yīng)當(dāng)先建立一個(gè)技術(shù)原型, 或者稱為“端到端”原型。以證明你的設(shè)計(jì)是能夠工作的。
你應(yīng)該在開發(fā)工作的早期做這些事情,因?yàn)椋绻浖脑O(shè)計(jì)方案是不可行的,在編碼實(shí)現(xiàn)階段無(wú)論采取什么措施都于事無(wú)補(bǔ)。技術(shù)原型將證明你的設(shè)計(jì)的可行性,從而,你的設(shè)計(jì)將更容易獲得支持。
17. 應(yīng)用已知的模式 目前,我們有大量現(xiàn)成的分析和設(shè)計(jì)模式以及問(wèn)題的解決方案可以使用。
一般來(lái)說(shuō),好的模型設(shè)計(jì)和開發(fā)人員,都會(huì)避免重新設(shè)計(jì)已經(jīng)成熟的并被廣泛應(yīng)用的東西。 http://www.ambysoft.com/processPatternsPage.html 收藏了許多開發(fā)模式的信息。
18. 研究每個(gè)模型的長(zhǎng)處和弱點(diǎn) 目前有很多種類的模型可以使用,用例捕獲的是系統(tǒng)行為需求,數(shù)據(jù)模型則描述支持一個(gè)系統(tǒng)運(yùn)行所需要的數(shù)據(jù)構(gòu)成。你可能會(huì)試圖在用例中加入實(shí)際數(shù)據(jù)描述,但是,這對(duì)開發(fā)者不是非常有用。同樣,數(shù)據(jù)模型對(duì)描述軟件需求來(lái)說(shuō)是無(wú)用的。每個(gè)模型在你建模過(guò)程中有其相應(yīng)的位置,但是,你需要明白在什么地方,什么時(shí)候使用它們。
19. 在現(xiàn)有任務(wù)中應(yīng)用多個(gè)模型 當(dāng)你收集需求的時(shí)候,考慮使用用例模型,用戶界面模型和領(lǐng)域級(jí)的類模型。
當(dāng)你設(shè)計(jì)軟件的時(shí)候,應(yīng)該考慮制作類模型,順序圖、狀態(tài)圖、協(xié)作圖和最終的軟件實(shí)際物理模型。
程序設(shè)計(jì)人員應(yīng)該慢慢意識(shí)到,僅僅使用一個(gè)模型而實(shí)現(xiàn)的軟件要么不能夠很好地滿足用戶的需求,要么很難擴(kuò)展。
20. 教育你的聽眾 你花了很大力氣建立一個(gè)很成熟的系統(tǒng)模型,而你的聽眾卻不能理解它們,甚至更糟-連為什么要先建立模型都不知道。那么你的工作是毫無(wú)意義的。 教給你開發(fā)人員基本的建模知識(shí);否則,他們會(huì)只看看你畫的漂亮圖表,然后繼續(xù)編寫不規(guī)范的程序。
另外, 你還需要告訴你的用戶一些需求建模的基礎(chǔ)知識(shí)。給他們解釋你的用例(uses case)和用戶界面模型,以使他們能夠明白你要表達(dá)地東西。當(dāng)每個(gè)人都能使用一個(gè)通用的設(shè)計(jì)語(yǔ)言的時(shí)候(比如UML-譯者注),你的團(tuán)隊(duì)才能實(shí)現(xiàn)真正的合作。
21. 帶工具的傻瓜還是傻瓜 你給我CAD/CAM工具,請(qǐng)我設(shè)計(jì)一座橋。但是,如果那座橋建成的話,我肯定不想當(dāng)?shù)谝粋€(gè)從橋上過(guò)的人,因?yàn)槲覍?duì)建筑一竅不通。
使用一個(gè)很優(yōu)秀的CASE工具并不能使你成為一個(gè)建模專家,只能使你成為一個(gè)優(yōu)秀CASE工具的使用者。成為一個(gè)優(yōu)秀的建模專家需要多年的積累,不會(huì)是一周針對(duì)某個(gè)價(jià)值幾千美元工具的培訓(xùn)。一個(gè)優(yōu)秀的CASE工具是很重要,但你必須學(xué)習(xí)使用它,并能夠使用它設(shè)計(jì)它支持的模型。
22. 理解完整的過(guò)程 好的設(shè)計(jì)人員應(yīng)該理解整個(gè)軟件過(guò)程,盡管他們可能不是精通全部實(shí)現(xiàn)細(xì)節(jié)。軟件開發(fā)是一個(gè)很復(fù)雜的過(guò)程,除了編程、建模、測(cè)試等你擅長(zhǎng)工作外,還有很多工作要做。好的設(shè)計(jì)者需要考慮全局。必須從長(zhǎng)遠(yuǎn)考慮如何使軟件滿足用戶需要,如何提供維護(hù)和技術(shù)支持等。
23. 常做測(cè)試,早做測(cè)試 如果測(cè)試對(duì)你的軟件來(lái)說(shuō)是無(wú)所謂的,那么你的軟件多半也沒(méi)什么必要被開發(fā)出來(lái)。建立一個(gè)技術(shù)原型供技術(shù)評(píng)審使用,以檢驗(yàn)?zāi)愕能浖P汀T谲浖芷谥校酵戆l(fā)現(xiàn)的錯(cuò)誤越難修改,修改成本越昂貴。盡可能早的做測(cè)試是很值得的。
24. 把你的工作歸檔 不值得歸檔的工作往往也不值得做。歸檔你的設(shè)想,以及根據(jù)設(shè)想做出的決定;歸檔軟件模型中很重要但不很明顯的部分。給每個(gè)模型一些概要描述以使別人很快明白模型所表達(dá)的內(nèi)容。
25. 技術(shù)會(huì)變,基本原理不會(huì) 如果有人說(shuō)“使用某種開發(fā)語(yǔ)言、某個(gè)工具或某某技術(shù),我們就不需要再做需求分析,建模,編碼或測(cè)試”。不要相信,這只說(shuō)明他還缺乏經(jīng)驗(yàn)。拋開技術(shù)和人的因素,實(shí)際上軟件開發(fā)的基本原理自20世紀(jì)70年代以來(lái)就沒(méi)有改變過(guò)。你必須還定義需求,建模,編碼,測(cè)試,配置,面對(duì)風(fēng)險(xiǎn),發(fā)布產(chǎn)品,管理工作人員等等。
軟件建模技術(shù)是需要多年的實(shí)際工作才能完全掌握的。好在你可以從我的建議開始,完善你們自己的軟件開發(fā)經(jīng)驗(yàn)。以雞湯開始,加入自己的蔬菜。然后,開始享受你自己的豐盛晚餐吧。
作者: Scott Ambler著 樂(lè)林峰 譯 來(lái)源: umlchina 風(fēng)之語(yǔ) 轉(zhuǎn)載
當(dāng)你使用CASE工具畫出包,類,屬性,方法和關(guān)系時(shí),AndroMDA的“概貌”就形成了。然后,你把模型保存為XMI格式,并用AndroMDA和XDoclet產(chǎn)生整個(gè)組件模型的Java源代碼。AndroMDA和XDoclet都可以和著名的構(gòu)建工具Ant進(jìn)行集成。你通常會(huì)使用Ant的命令行版本,但也可以在IDE如Eclipse或JBuilder中使用Ant。無(wú)論是哪一種方式,你都要使用自己定制的Ant構(gòu)建腳本,并在腳本中把AndroMDA定義為其中的一個(gè)Task。 在CASE工具中用UML建模 你可以使用UML的圖形符號(hào)為現(xiàn)實(shí)世界中的事物建模。例如,考慮一個(gè)汽車租賃系統(tǒng),用于管理客戶,司機(jī),汽車和租賃合同。這個(gè)系統(tǒng)UML模型的類圖可能是如下的樣子:

將模型保存為XMI格式。某些CASE工具把這稱為“export”,用于區(qū)別它本身私有的格式。 代碼生成器 從XMI模型中產(chǎn)生代碼,Ant構(gòu)建腳本經(jīng)過(guò)以下的步驟:
- Ant Task讀入XMI格式的UML模型,并在內(nèi)存中生成抽象的語(yǔ)法樹(abstract syntax tree),其中包含包,類,屬性,方法和關(guān)系的信息。
- 然后,使用Velocity模板處理引擎來(lái)處理entity bean,session bean,Hibernate類或別的代碼生成模板,所有的模板都基于從CASE工具中產(chǎn)生的抽象語(yǔ)法樹。它使用一個(gè)腳本helper facade來(lái)屏蔽UML元模型的復(fù)雜性,因此模板的開發(fā)者可以使用容易理解的API來(lái)為腳本寫代碼。這些步驟會(huì)產(chǎn)生一些源代碼文件。
- 最后,Ant腳本調(diào)用XDoclet中的<ejbDoclet>或<hibernateDoclet> Task。對(duì)于EJB,XDoclet task讀入所有的bean類信息并生成bean的接口和部署配置文件。對(duì)于Hibernate,XDoclet生成包含持久層映射信息的XML文件。
使用Cartridges定制輸出 到此為止,你可能以為AndroMDA是一個(gè)EJB或Hibernate JavaBean的生成器。其實(shí),AndorMDA可以生成任何東西! 事實(shí)上,AndroMDA對(duì)于它生成的東西一無(wú)所知。它擁有一個(gè)稱為“cartridges”的可插入模塊。一個(gè)Cartridge由一套定義生成格式的模板文件組成。目前,AndroMDA包括四個(gè)cartridge:
- andromda-java - 生成一般的Java源代碼。
- andromda-ejb - 生成EJB。
- andromda-hibernate - 生成Hibernate ORM工具的持久層類。
- andromda-struts - 生成Jakarta Struts的web頁(yè)面,form bean和action類。
你可以選擇使用哪一個(gè)cartridge來(lái)產(chǎn)生你的應(yīng)用框架。你也可以編寫你自己的cartridge - 一旦你理解了cartridge的基礎(chǔ)知識(shí)及其XML描述文件,編寫一個(gè)新的cartridge非常容易! AndroMDA核心自動(dòng)檢測(cè)安裝在類路徑下的cartridge。如需要了解更多cartridge的信息,請(qǐng)參見本網(wǎng)站的其他文檔。 EJB Cartridge生成代碼的例子 從上面汽車租賃系統(tǒng)模型的客戶模型部分,AndroMDA(使用andromda-ejb)和XDoclet將為你產(chǎn)生下面的代碼。Bean類使用
標(biāo)記,其他類使用
標(biāo)記。你可以點(diǎn)擊文件名查看文件的內(nèi)容。
編寫業(yè)務(wù)方法 你可能知道,使用代碼生成器并沒(méi)有完成了所有的工作。編寫B(tài)ean的主體即業(yè)務(wù)邏輯是留給你的工作。AndroMDA為你構(gòu)建了一個(gè)應(yīng)用框架,你需要往里面填充代碼。這些所謂的“implementation classes”來(lái)自bean類,是類繼承結(jié)構(gòu)樹上的葉子。AndroMDA一次性地產(chǎn)生這些代碼并不再修改它們。這能夠確保手工編寫的代碼不被代碼生成器覆蓋。 因此,在實(shí)現(xiàn)類中實(shí)現(xiàn)你的業(yè)務(wù)方法,并啟動(dòng)Ant構(gòu)建腳本用于編譯Java文件的其他task,并把編譯好的class文件打包到一個(gè)ejb-jar文件中。jar文件當(dāng)然也包含了生成的部署配置文件。 最后的工作 最后的工作當(dāng)然是發(fā)布到應(yīng)用服務(wù)器上。以JBoss為例,只需簡(jiǎn)單地將jar文件復(fù)制到JBoss的部署路徑中即可。
在中國(guó)的同行中經(jīng)常看到:要么不重視軟件架構(gòu),要么過(guò)分重視乃至捧為天書。如果要是碰到一個(gè)不懂技術(shù)的項(xiàng)目經(jīng)理強(qiáng)行推行新技術(shù)有會(huì)有何種效果呢?
其實(shí)在做系統(tǒng)架構(gòu)時(shí),有時(shí)候會(huì)常常忽略以下幾點(diǎn):
1:軟件的架構(gòu)在大的方向上是固定的。
這種固定依據(jù)某些基本固定的軟件模式(包括設(shè)計(jì)模式、編碼模式或者某些特定的約定)來(lái)強(qiáng)化和固定。這使得軟件開發(fā)在某種程度上具有某些可以明顯看得到的風(fēng)格,這個(gè)風(fēng)格被整個(gè)的項(xiàng)目貫穿和堅(jiān)持,這樣當(dāng)我們進(jìn)入通常可怕的維護(hù)或者調(diào)整的時(shí)候,我們不會(huì)害怕我們?cè)诤芏喾N不同的實(shí)現(xiàn)中常常迷失了方向。
2:軟件的架構(gòu)在具體的應(yīng)用中可以適度的可控靈活。
毫無(wú)疑問(wèn),軟件設(shè)計(jì)開發(fā)不可能沒(méi)有例外,在某種程度上保留一些適度的靈活時(shí)必要的。一方面,它符合軟件開發(fā)的實(shí)際;一方面,適度的可控靈活體現(xiàn)了一種對(duì)實(shí)現(xiàn)者的尊重和信任。然而,如何實(shí)現(xiàn)可控的靈活呢?通常,我認(rèn)為可以考慮有限種類的設(shè)計(jì)選擇并通過(guò)有色彩的圖形來(lái)表明這種可選擇性。但即便如此,設(shè)計(jì)者很重要的要考慮這些不同選擇之間的關(guān)系,以及這些選擇和整體設(shè)計(jì)的兼容性,這個(gè)時(shí)候,面向接口的設(shè)計(jì)和適度的“粘合”設(shè)計(jì)是必要的。
3:架構(gòu)設(shè)計(jì)的支撐。
架構(gòu)設(shè)計(jì)通常會(huì)涉及到一些支撐設(shè)計(jì),這些設(shè)計(jì)和實(shí)現(xiàn)水準(zhǔn)直接的體現(xiàn)了系統(tǒng)的最有價(jià)值的部分,更細(xì)的劃分我們應(yīng)該可以看到性能和結(jié)構(gòu)相關(guān)的部分,以及必要的基礎(chǔ)類。通常,作為設(shè)計(jì)人員,我們應(yīng)該能夠設(shè)計(jì)并實(shí)現(xiàn)系統(tǒng)的性能、結(jié)構(gòu)、擴(kuò)展、粘合等部分的工作,這部分的工作通常不應(yīng)該離開設(shè)計(jì)人員的控制和把握,這些代碼的書寫或者至少積極的關(guān)注是設(shè)計(jì)人員必要的素質(zhì):我們不應(yīng)該讓更多的看到我們無(wú)法寫出代碼來(lái)(我也反對(duì)沒(méi)有分工什么都做的設(shè)計(jì)人員!),實(shí)際上,這是軟件最困難也是最有樂(lè)趣的地方。特別是在測(cè)試結(jié)果中發(fā)現(xiàn)這些設(shè)計(jì)實(shí)現(xiàn)帶來(lái)的性能的極大提高的時(shí)候是非常有成就感的事情。至于基礎(chǔ)類,應(yīng)該是盡可能的減少依賴和耦合的。架構(gòu)設(shè)計(jì)的支撐要盡可能的對(duì)客戶程序員隱藏不必要的中間細(xì)節(jié),對(duì)基礎(chǔ)類要盡量簡(jiǎn)單、穩(wěn)定并真正需要。
通常,新興的技術(shù)會(huì)用在架構(gòu)設(shè)計(jì)的支撐設(shè)計(jì)里面并被封裝以隱藏具體的實(shí)現(xiàn),而通盤應(yīng)用新興技術(shù)的風(fēng)險(xiǎn)是巨大的,并且對(duì)人員的獲得也是問(wèn)題。有些技術(shù)是可以提供替代技術(shù)的,一些新興的技術(shù)實(shí)現(xiàn)也是可以參考的(但未必采用她的實(shí)現(xiàn))。要記住軟件開發(fā)是需要速度、質(zhì)量、可維護(hù)而不是技術(shù)表演,這個(gè)度應(yīng)該由分析設(shè)計(jì)人員負(fù)責(zé)任的來(lái)把握。
4:面向業(yè)務(wù)還是面向頁(yè)面的。
也許這種說(shuō)法會(huì)招致一些人的反對(duì),認(rèn)為自然而然的應(yīng)該是前者,然而,實(shí)際的情況是,我們常常看到打著業(yè)務(wù)的幌子實(shí)現(xiàn)為面向頁(yè)面的系統(tǒng),原因很簡(jiǎn)單:后者更加的“簡(jiǎn)單”并似乎有更少的代碼和工作量。這在某種程度上是對(duì)的。然而,區(qū)分這兩種不同的設(shè)計(jì)是必要的,盡管你也可能使用了MVC2的架構(gòu),但你可以做出面向頁(yè)面的設(shè)計(jì)的。觀察我們的軟件開發(fā),最先提交的代碼未必是最少的代價(jià)的代碼:我們常常可以發(fā)現(xiàn)一些項(xiàng)目總是在那些不起眼的地方不斷的“修改”,我們的程序員因此好像很忙,但對(duì)我們的項(xiàng)目對(duì)公司來(lái)說(shuō)這是糟糕的不必要開銷。因此,考慮這兩種可能的情況,在做出選擇之前加以思考是必要的。
5:用團(tuán)隊(duì)和別人的眼光考慮問(wèn)題。
實(shí)際上,每個(gè)項(xiàng)目都是有所差異的,特別是人的差異。很糟糕的是,我們通常不得不面對(duì)人力資源的極大壓力,特別是這些人之間常常都沒(méi)有共事的經(jīng)驗(yàn)也就是常常是臨時(shí)組成的,而這些人員也通常也缺乏一個(gè)軟件開發(fā)的基本共識(shí):對(duì)標(biāo)準(zhǔn)的遵從哪怕是最簡(jiǎn)單的Java編碼規(guī)范和對(duì)自己提交的產(chǎn)品的簡(jiǎn)單備注和測(cè)試都是缺乏的而通常又沒(méi)有達(dá)到代碼就是文檔的水準(zhǔn)。在這個(gè)時(shí)候,用團(tuán)隊(duì)和別人的眼光考慮問(wèn)題就是必要的,這實(shí)際上就是取技術(shù)上的“交集”,也就是走簡(jiǎn)單的路線。如果沒(méi)有選擇的時(shí)候,培訓(xùn)工作就是非常必要的。
用團(tuán)隊(duì)和別人的眼光考慮問(wèn)題也會(huì)有其它的問(wèn)題,特別是當(dāng)項(xiàng)目壓力如進(jìn)度壓力等來(lái)臨的時(shí)候或者項(xiàng)目成員根本不考慮公司整體和長(zhǎng)期利益的時(shí)候,這個(gè)問(wèn)題很突出,有時(shí)候作為分析設(shè)計(jì)人員你甚至很難解決,特別是你面臨剛才我所提到的“沒(méi)有Java開發(fā)實(shí)際經(jīng)驗(yàn)甚至缺乏軟件開發(fā)經(jīng)驗(yàn)的項(xiàng)目經(jīng)理試圖控制技術(shù)的時(shí)候”更加如此。
軟件架構(gòu)的設(shè)計(jì)說(shuō)起來(lái)簡(jiǎn)單,也可以說(shuō)起來(lái)復(fù)雜,然而,如何把事情做到簡(jiǎn)單并且把其中的針對(duì)性能、分層、粘合、擴(kuò)展等處理好,并提供可控的靈活性不是容易的。通常,只有多一些經(jīng)驗(yàn)和教訓(xùn)并能夠在軟件代碼上加以實(shí)現(xiàn)核心的才可能成為好的分析設(shè)計(jì)人員。
在此演練中,您將在 Sun Java? Studio Creator 應(yīng)用程序開發(fā)環(huán)境 (IDE) 中創(chuàng)建和部署一個(gè)簡(jiǎn)單的 Web 應(yīng)用程序
"travel center"。向頁(yè)面中添加兩個(gè)組件,并將那些數(shù)據(jù)識(shí)別組件綁定到本地?cái)?shù)據(jù)庫(kù)。然后,生成應(yīng)用程序并將其
部署到本地應(yīng)用服務(wù)器,在客戶機(jī) Web 瀏覽器上運(yùn)行該應(yīng)用程序。
內(nèi)容
? 創(chuàng)建項(xiàng)目
? 添加 Dropdown List 組件
? 將組件連接到數(shù)據(jù)庫(kù)
? 運(yùn)行 Web 應(yīng)用程序
? 添加 Data Table
? 修改 SQL 查詢
? 控制顯示的行
要完成此演練,系統(tǒng)上應(yīng)該已經(jīng)安裝了 IDE,并且您已經(jīng)閱讀了 Java Studio Creator 入門 教程。
創(chuàng)建項(xiàng)目
開發(fā) Web 應(yīng)用程序從創(chuàng)建項(xiàng)目開始。就像在入門教程中學(xué)習(xí)的那樣,項(xiàng)目是 IDE 中的基本工作單元。項(xiàng)目包含
組成應(yīng)用程序的所有源代碼和資源。
1. 在“歡迎”屏幕上單擊“創(chuàng)建新項(xiàng)目”。
2. 在“新建項(xiàng)目”對(duì)話框的“名稱”文本字段中,鍵入 TravelCenter。
3. 選擇“缺省 J2EE Web 應(yīng)用程序”模板(如果尚未選定),然后單擊“確定”。
通過(guò)這些步驟會(huì)創(chuàng)建兩個(gè)重要的文件,在本教程的其他部分中也將用到這兩個(gè)文件。
? Page1.jsp-JavaServer Pages? 中包含組成 Web 應(yīng)用程序的組件。最初,在應(yīng)用程序中只有一個(gè) JSP?
頁(yè),但是隨后您可以添加更多的頁(yè)面。
? Page1.java-包含頁(yè)面狀態(tài)并使其在不同的呈現(xiàn)中保持一致的 JavaBeans? Bean。缺省情況下代碼是不
可見的,但是您可以通過(guò)右鍵單擊頁(yè)面,然后在上下文菜單中選擇“查看 Page1 Java 類”來(lái)顯示它。
添加 Dropdown List 組件
接下來(lái),向頁(yè)面中添加 Dropdown List 組件。
1. 在組件面板中,選擇“JSF”>“JSF 標(biāo)準(zhǔn)組件”,然后將 Dropdown List 組件拖到頁(yè)面上。
Dropdown List 組件將出現(xiàn)在 Page1.jsp 頁(yè)面上。您可以通過(guò)使用選擇句柄來(lái)調(diào)整組件的大小,也可以
1
Sun Java Studio Creator
將組件移動(dòng)到新位置。
2. 右鍵單擊頁(yè)面背景,然后在上下文菜單中選擇“查看 Page1 Java 類”。
會(huì)在源編輯器中打開頁(yè)面的 JavaBeans 源代碼。
3. 在“選擇類成員”下拉列表中選擇 "dropdown1" 以導(dǎo)航到實(shí)例變量聲明。(“選擇類成員”下拉列表位于
源編輯器工具欄中。)
初始化 Dropdown List 組件的 Java 代碼已添加到 Page1 類實(shí)現(xiàn)。
private HtmlSelectOneMenu dropdown1 = new HtmlSelectOneMenu();
將組件連接到數(shù)據(jù)庫(kù)
在此部分中,使 Dropdown List 組件可以從數(shù)據(jù)庫(kù)表獲得項(xiàng)。
1. 單擊源編輯器頂部的 Page1.jsp 標(biāo)簽以查看頁(yè)面。
2. 從“服務(wù)器導(dǎo)航”中,將“數(shù)據(jù)源”> "Travel" >“表”> "PERSON" 節(jié)點(diǎn)拖放到 Dropdown List 組件的上
面。
將出現(xiàn)一個(gè)對(duì)話框,且 personRowSet 已添加到非可視組件托盤中。
注如果 PointBase Embedded 數(shù)據(jù)庫(kù)沒(méi)有運(yùn)行,則您將看到一個(gè)“錯(cuò)誤”對(duì)話框,告訴您這一情況。如果看
到此對(duì)話框,請(qǐng)關(guān)閉“錯(cuò)誤”對(duì)話框,在“服務(wù)器導(dǎo)航”中右鍵單擊“PointBase 數(shù)據(jù)庫(kù)服務(wù)器”節(jié)點(diǎn),
然后在上下文菜單中選擇“啟動(dòng) PointBase”。然后,右鍵單擊“數(shù)據(jù)源”>
"Travel",并從上下文菜單中選擇 “刷新”。現(xiàn)在,您應(yīng)該能夠定位到 "Travel" 數(shù)據(jù)源節(jié)點(diǎn)。
3. 單擊“填充列表”。
因?yàn)?PERSON.NAME 列屬于 SQL 類型 varchar,在 Dropdown List 組件中將顯示文本 "abc",指示所顯
示的數(shù)據(jù)是一個(gè)字符串。
注如果沒(méi)看到這個(gè)對(duì)話框,是因?yàn)?PERSON 節(jié)點(diǎn)沒(méi)有被直接放到 Dropdown List 組件的上面。在這種情況
下,在執(zhí)行下一步時(shí),請(qǐng)確保如圖 1 所示選定了 PERSON.PERSONID 和 PERSON.NAME 列。
4. 右鍵單擊 Dropdown List 組件,然后從上下文菜單中選擇“從數(shù)據(jù)庫(kù)填充列表”。
“值字段”被綁定到 PERSON.PERSONID 列,該列是 PERSON 表的主鍵。此字段提供由 JSF 組件的
getValue() 方法返回的值。另一方面,“顯示字段”被綁定到 PERSON.NAME 列,是組件運(yùn)行時(shí)所顯
示的內(nèi)容。
5. 單擊“確定”。
運(yùn)行 Web 應(yīng)用程序
現(xiàn)在,您可以部署和運(yùn)行 Web 應(yīng)用程序了。IDE 附帶有一個(gè)已經(jīng)安裝的本地部署服務(wù)器:
? Sun Java? System Application Server Platform Edition 8
部署服務(wù)器是作為“服務(wù)器導(dǎo)航”中“部署服務(wù)器”節(jié)點(diǎn)下的一個(gè)節(jié)點(diǎn)安裝的。通過(guò)部署服務(wù)器的上下文菜單
(可通過(guò)右鍵單擊它進(jìn)行訪問(wèn)),可以啟動(dòng)或停止它。
部署和運(yùn)行 Web 應(yīng)用程序
1. 單擊工具欄中的“全部保存”按鈕。
2. 單擊工具欄中的“運(yùn)行項(xiàng)目”按鈕。
IDE 將生成、部署和運(yùn)行 TravelCenter 應(yīng)用程序。首先,“生成輸出”窗口出現(xiàn)在 IDE 布局的底部。有關(guān)
編譯的信息和部署準(zhǔn)備將輸出到此窗口。(如果在生成時(shí)出現(xiàn)問(wèn)題,請(qǐng)首先檢查“生成輸出”窗口。)接
下來(lái),將打開一個(gè)對(duì)話框,其中顯示部署的狀態(tài)。
部署之后,將使用 URL
http://localhost:18080/travelcenter/ 為應(yīng)用程序打開一個(gè)新的 Web 瀏覽器
窗口。使用 PERSON 表的 NAME 列中的數(shù)據(jù)填充下拉列表。
添加 Data Table
接下來(lái),向應(yīng)用程序中添加一個(gè) Data Table 組件,并將該組件與數(shù)據(jù)庫(kù)表相連。
1. 在組件面板中,單擊“JSF 標(biāo)準(zhǔn)組件”,將 Data Table 拖至頁(yè)面,并將它放置在 Dropdown List 組件的下
面。
2. 拖動(dòng)“數(shù)據(jù)源”> "Travel" >“表”> "TRIP" 節(jié)點(diǎn),并將其放在 Data Table 上。確保整個(gè) Data Table 組件的輪
廓為藍(lán)色時(shí)放置節(jié)點(diǎn)。如果只有列或列標(biāo)題的輪廓為藍(lán)色,請(qǐng)將節(jié)點(diǎn)在組件內(nèi)四處移動(dòng),直到整個(gè)組件的
輪廓為藍(lán)色。
如果將節(jié)點(diǎn)放在列或列標(biāo)題上,會(huì)出現(xiàn)“選擇目標(biāo)”對(duì)話框,而且 tripRowSet 已添加到非可視組件托
盤中。確保選中了 "dataTable1" 單選按鈕,然后單擊“確定”關(guān)閉對(duì)話框。
3. 右鍵單擊 Data Table 組件,然后選擇“表布局”。
注如果沒(méi)有選定 Data Table 組件,或者選定了它的其中一個(gè)子組件(在本例中是列),則在右鍵單擊 Data
Table 時(shí),將出現(xiàn)另外的上下文菜單。在這種情況下,請(qǐng)選擇 "dataTable1" >“表布局”。您可以按 Esc
鍵選擇當(dāng)前所選組件的父組件。
出現(xiàn)“表布局”對(duì)話框。這兩個(gè)列表表明哪些列可用于顯示以及正在顯示哪些內(nèi)容。將 TRIP 表放在 Data
Table 組件上時(shí),將選擇所有可用的列進(jìn)行顯示。
4. 選擇“顯示”列表中的第一列 (TRIP.TRIPID),然后單擊 "<" 按鈕。
將從“顯示”列表中移除該列。
5. 移除 TRIP.PERSONID 和 TRIP.TRIPTYPEID 列。
仍然出現(xiàn)在“顯示”列表中的三列:
? TRIP.DEPDATE
? TRIP.DEPCITY
? TRIP.DESTCITY
使用數(shù)據(jù)綁定組件訪問(wèn)數(shù)據(jù)庫(kù) ?
Sun Java Studio Creator
6. 單擊“確定”。
現(xiàn)在,Data Table 組件中有三個(gè)顯示列。
修改 SQL 查詢
在此部分中,將修改 TRIP 行集對(duì)象中的 SQL 查詢,以便查詢返回 TRIPTYPE 表中的數(shù)據(jù)。您還將修改 Data
Table 組件以顯示新列。
1. 右鍵單擊非可視組件托盤中的 tripRowSet,然后選擇“編輯行集查詢”。
在編輯器窗格中將出現(xiàn)查詢編輯器。標(biāo)簽的名稱是 tripRowSet。
2. 右鍵單擊設(shè)計(jì)視圖(見圖 4),然后選擇“添加表”。
3. 選擇 TRAVEL.TRIPTYPE 表,然后單擊“確定”。
將出現(xiàn)另一個(gè)表格圖,在兩個(gè)表格圖之間有一個(gè)鏈接。
4. 在指示的表中取消選中以下復(fù)選框:
? TRIP 表中的 TRIPID
? 兩個(gè)表中的 TRIPTYPEID
會(huì)從結(jié)果集中刪除上述列。同時(shí)會(huì)修改源視圖中的 SQL 查詢以反映這些更改。
4 使用數(shù)據(jù)綁定組件訪問(wèn)數(shù)據(jù)庫(kù) ?
圖 2 顯示的列
圖 3 第 1 列處于選定狀態(tài)的 Data Table
Sun Java Studio Creator
5. 選擇 Page1.jsp 標(biāo)簽以返回到可視編輯器。
6. 右鍵單擊 Data Table,然后選擇“表布局”。
將出現(xiàn)“表布局”對(duì)話框。由于您已經(jīng)更改了行集的 SQL 查詢,因此有更多可以顯示的列。
7. 將 TRIPTYPE.DESCRIPTION 列添加到“顯示”列表中。
8. 單擊“確定”。
第四列將出現(xiàn)在 Data Table 組件中。
控制顯示的行
在上一部分中將 TRIP 表放在 Data Table 組件上時(shí),IDE 使用返回表中所有列的所有行的 SQL 查詢創(chuàng)建了一個(gè)
行集對(duì)象。如果此時(shí)部署應(yīng)用程序,則數(shù)據(jù)表將包含 TRIP 表中的所有行程信息。
假定您僅希望顯示其名字出現(xiàn)在 Dropdown List 組件中人員的行程信息。您必須通過(guò)編輯 TRIP 行集對(duì)象的缺省查
詢,在 Dropdown List 組件和 Data Table 組件之間創(chuàng)建主從關(guān)系 (Master-Detail)。
1. 雙擊 tripRowSet 對(duì)象打開查詢編輯器。
2. 在查詢編輯器的設(shè)計(jì)網(wǎng)格(電子表格)中,右鍵單擊設(shè)計(jì)網(wǎng)格 PERSONID 行中的“條件”單元格,然后
選擇“添加查詢條件”。
使用數(shù)據(jù)綁定組件訪問(wèn)數(shù)據(jù)庫(kù) ?
圖 4 查詢編輯器
設(shè)計(jì)視圖
設(shè)計(jì)網(wǎng)格
源代碼
Sun Java Studio Creator
3. 將“比較”下拉菜單設(shè)置為“=等于”,并選擇“參數(shù)”單選按鈕。
4. 單擊“確定”。
在 PERSONID 的“條件”列中您會(huì)看到 "=?",它在 SQL 查詢中添加了以下 WHERE 子句:
WHERE TRAVEL.TRIP.PERSONID = ?
5. 通過(guò)選擇 Page1.jsp 標(biāo)簽,返回到可視編輯器,然后雙擊 Dropdown List 組件。
Page1 類的源代碼在編輯器區(qū)域中打開,且光標(biāo)位于 dropdown1_processValueChange() 方法主體
內(nèi)。此事件處理程序方法存根 (Stub) 是在您首次雙擊 Dropdown List 組件時(shí)創(chuàng)建的。
6. 從“組件面板”>“代碼片段”>“演示”拖動(dòng) Travel dropdown 代碼片段,將其放在
dropdown1_processValueChange() 方法體中。
6 使用數(shù)據(jù)綁定組件訪問(wèn)數(shù)據(jù)庫(kù) ?
圖 5 “添加查詢條件”對(duì)話框
Sun Java Studio Creator
public void dropdown1_processValueChange(ValueChangeEvent vce) {
// User event code here...
try {
dataTable1Model.setObject(1, dropdown1.getValue());
dataTable1Model.execute();
} catch (Exception e) {
log("person change exception", e);
error(“Exception changing person id:”+e);
} // end try catch
}
這段代碼將下拉列表的值綁定到為 dataTable1Model 準(zhǔn)備的 SQL 語(yǔ)句中的參數(shù)上。
7. 在 Page1 類中找到 Page1() 構(gòu)造函數(shù)。
8. 從“組件面板”>“代碼片段”>“演示”拖動(dòng) Travel initialization 代碼片段,并將其放在 Page1() 構(gòu)造
函數(shù)實(shí)現(xiàn)的結(jié)尾處。
public Page1() {
// other lines of code omitted
catch ( Exception e) {
log("Page1 Initialization Failure", e);
throw new FacesException(e);
}
// Additional user provided initialization code
try {
personRowSet.execute();
personRowSet.next();
dataTable1Model.setObject(1, personRowSet.getObject("PERSONID"));
} catch (Exception ex) {
throw new FacesException(ex);
} // end try catch
}
這段代碼將把下拉列表中當(dāng)前選定 NAME 的 PERSONID 值綁定到為 dataTable1Model 準(zhǔn)備的 SQL 語(yǔ)句
中的參數(shù)上。
9. 通過(guò)選擇 Page1.jsp 標(biāo)簽返回到可視編輯器。
10.右鍵單擊 Dropdown List 組件,然后選擇“更改時(shí)自動(dòng)提交”。
在屬性表單中,以下代碼將出現(xiàn)在 "Javascript" > "onchange" 屬性中:
this.form.submit();
現(xiàn)在,當(dāng)用戶在運(yùn)行的 Web 應(yīng)用程序中更改下拉列表選擇時(shí),將重新提交和更新頁(yè)面。
11.單擊工具欄上的“全部保存”。
12.單擊工具欄上的“運(yùn)行項(xiàng)目”。
將重新生成和部署 Web 應(yīng)用程序。在下拉列表中選擇另一個(gè)名字,您會(huì)注意到更新了數(shù)據(jù)表。
使用數(shù)據(jù)綁定組件訪問(wèn)數(shù)據(jù)庫(kù) ? 2004 6 年月 7
Sun Java Studio Creator
請(qǐng)參見
? Java Studio Creator 入門
本文下載地址: http://mail.yl.gov.cn/ftp/yy/jscb2005.rar
基于JSF的Ajax實(shí)現(xiàn)AjaxFaces發(fā)布了1.0版本。
AjaxFaces項(xiàng)目是由CyberXP.net提供的。該項(xiàng)目使JSF的UI組件具備了Ajax的能力,可以觸發(fā)Ajax過(guò)程,也可以在Ajax處理過(guò)程中更新界面組件。AjaxFaces還提供了一組功能性的組件,包括樹、日歷等,這些組件都內(nèi)建了Ajax功能。
下載地址:http://cyberxp.net/
作者: Programmer's Life
B/S 作為如今最為流行的體系結(jié)構(gòu)模式,也是受到了廣大開發(fā)人員以及客戶的認(rèn)同,其開發(fā)模式也在不斷的發(fā)展著,在這里主要就 Java B/S 的開發(fā)模式做一番回顧和探討,也算是自己對(duì)于 Java B/S 開發(fā)模式的一種總結(jié)。
Jsp+Jdbc
在 B/S 開發(fā)中最簡(jiǎn)單的一種開發(fā)模式是頁(yè)面 + 邏輯處理,映射到技術(shù)上反應(yīng)出來(lái)的有 Jsp+Jdbc ,在基于這類的實(shí)現(xiàn)中在 View 層也就是 jsp 頁(yè)面上負(fù)責(zé)數(shù)據(jù)的顯示、邏輯處理,結(jié)合 jdbc 完成數(shù)據(jù)的持久化,在小型的項(xiàng)目中,人們確實(shí)發(fā)現(xiàn)這種方式是最為方便的,但在復(fù)雜的項(xiàng)目以及需求不斷變化的項(xiàng)目中,人們慢慢的發(fā)現(xiàn)這種方式造成了不少的問(wèn)題,首先是調(diào)試的問(wèn)題,想想在一個(gè) jsp 頁(yè)面中進(jìn)行排錯(cuò)是多么的困難,其次是修改的問(wèn)題,為了滿足用戶需求的一個(gè)小小的變化,都需要去改不少的頁(yè)面,而且很多時(shí)候由于寫的時(shí)間長(zhǎng)了,自己都需要回憶很久才能想起是怎么回事,更不用說(shuō)如果人員流動(dòng)了會(huì)怎么樣,同時(shí)還帶來(lái)開發(fā)效率的問(wèn)題,由于需要缺少足夠的調(diào)試的支持,需要較為熟練的開發(fā)人員才能快速的完成,對(duì)于一般的人員來(lái)說(shuō)需要一定的適應(yīng)和學(xué)習(xí)過(guò)程,當(dāng)然伴隨而來(lái)的還有諸如修改界面的時(shí)候一不小心少 copy 了點(diǎn)代碼什么造成的錯(cuò),最大的問(wèn)題可能還是重用的問(wèn)題,通常會(huì)造成 N 多同樣的代碼在頁(yè)面上 copy 來(lái) copy 去的,總結(jié)下來(lái)在這種模式下有幾個(gè)比較重大的問(wèn)題就是:
1、 調(diào)試問(wèn)題。
2、 維護(hù)問(wèn)題,顯示和邏輯處理在一起導(dǎo)致了修改顯示的時(shí)候較為困難,至于修改代碼則因?yàn)橹暗恼{(diào)試問(wèn)題導(dǎo)致了困難,同時(shí)由于邏輯均在頁(yè)面上后期接手人員需要一段時(shí)間去理解。
3、 代碼重用性問(wèn)題。
但同樣它還是存在優(yōu)點(diǎn)的,那就是可以很快的上手,但由于調(diào)試和維護(hù)性問(wèn)題確實(shí)太大了,所以在現(xiàn)在也是基本不再采用這種方式了。
Jsp+JavaBean
在經(jīng)歷了 jsp+jdbc 階段后,開始考慮怎么樣去解決上面三個(gè)問(wèn)題,這個(gè)時(shí)候就誕生了諸 JSP+JavaBean 這樣的技術(shù)體系,在這個(gè)體系中由 jsp 頁(yè)面負(fù)責(zé)顯示以及接收頁(yè)面請(qǐng)求,并調(diào)用相應(yīng)的 JavaBean 來(lái)完成邏輯處理,在獲取其返回的處理數(shù)據(jù)后轉(zhuǎn)到相應(yīng)的頁(yè)面進(jìn)行顯示。在這樣的技術(shù)體系中,由于邏輯是由 JavaBean 來(lái)完成的,可以對(duì)其進(jìn)行調(diào)試了,代碼的重用性一定程度上也得到了提高。剛開始的時(shí)候用這樣的技術(shù)體系確實(shí)發(fā)現(xiàn)比以前用 jsp+jdbc 爽了很多,但隨著用多了,慢慢又發(fā)現(xiàn)了問(wèn)題,那就是在頁(yè)面中需要編寫對(duì)于頁(yè)面請(qǐng)求數(shù)據(jù)的獲取,還得根據(jù)請(qǐng)求去調(diào)用相應(yīng)的 javabean ,并根據(jù) javabean 的處理結(jié)果轉(zhuǎn)入相應(yīng)的頁(yè)面,這同樣造成了修改的麻煩,畢竟是去頁(yè)面上修改這些邏輯,總結(jié)下來(lái)在這種模式下有比較重大的問(wèn)題就是:
1、 代碼重用性以及維護(hù)性問(wèn)題。但這里的代碼重用性問(wèn)題和 jsp+jdbc 的就不同,在邏輯處理部分現(xiàn)在已經(jīng)可以重用了,但現(xiàn)在在各個(gè)頁(yè)面就不得不重復(fù)的寫獲取頁(yè)面請(qǐng)求的參數(shù)、相應(yīng)的調(diào)用 Model 、根據(jù) Model 的處理結(jié)果轉(zhuǎn)發(fā)頁(yè)面,這樣的話就導(dǎo)致了在改的時(shí)候需要到處去找,造成了維護(hù)的復(fù)雜。
2、 系統(tǒng)結(jié)構(gòu)不清晰。畢竟仍然是在頁(yè)面控制整個(gè)響應(yīng)頁(yè)面事件的處理流程,這個(gè)時(shí)候就造成了很多頁(yè)面中出現(xiàn)完全相同的 jsp 代碼,而且控制代碼在頁(yè)面,仍然是不便操作,例如對(duì)于 JavaBean 的調(diào)用等,而且由于獲取 javabean 的數(shù)據(jù)需要轉(zhuǎn)發(fā)的緣故,其實(shí)通常就是在最終的顯示頁(yè)面上加上上面的控制事件處理流程的代碼,并沒(méi)有真正的做到顯示和處理的分離。
同樣,它的優(yōu)點(diǎn)在于分離了顯示和業(yè)務(wù)邏輯處理,增強(qiáng)了可調(diào)試以及維護(hù)性,而且也是很容易上手的,對(duì)于小型項(xiàng)目來(lái)說(shuō)仍然是可選的方案之一。
基于 MVC Framework
在經(jīng)歷了上面的 Jsp+JavaBean 后,我們發(fā)現(xiàn)其實(shí)現(xiàn)在最需要的就是在 jsp 、 javabean 之間能有個(gè)東西自動(dòng)完成頁(yè)面請(qǐng)求數(shù)據(jù)的封裝、根據(jù)請(qǐng)求調(diào)用相應(yīng)的 javabean 、同時(shí)根據(jù) javabean 的處理結(jié)果返回至相應(yīng)的 View ,有了這樣的思想后,發(fā)現(xiàn) smalltalk 中的 MVC 思想很適合這種場(chǎng)景,于是便在 Java B/S 開發(fā)中引入了 MVC 思想,在這里也簡(jiǎn)單的介紹下 MVC 思想, MVC 強(qiáng)調(diào) View 和 Model 的分離, View 所面對(duì)的是 Controller ,由 Controller 負(fù)責(zé)與 Model 進(jìn)行交互, View 只負(fù)責(zé)顯示頁(yè)面以及顯示邏輯的處理,顯示邏輯指的是諸如第一行要顯示藍(lán)色、第二行要顯示紅色這樣的顯示方面的處理, Controller 負(fù)責(zé)接受頁(yè)面請(qǐng)求,并將其請(qǐng)求數(shù)據(jù)進(jìn)行封裝,同時(shí)根據(jù)請(qǐng)求調(diào)用相應(yīng)的 Model 進(jìn)行邏輯處理,在 Model 處理后返回結(jié)果數(shù)據(jù)到 Controller , Controller 將根據(jù)此數(shù)據(jù)調(diào)用相應(yīng)的 View ,并將此數(shù)據(jù)傳遞給 View ,由 View 負(fù)責(zé)將數(shù)據(jù)進(jìn)行融合并最終展現(xiàn)。 MVC 帶來(lái)的優(yōu)點(diǎn)很明顯的體現(xiàn)出來(lái)了,基于一個(gè)這樣的 MVC Framework 的話開發(fā)人員可以按照一種固定的模式進(jìn)行開發(fā),規(guī)范了整個(gè)開發(fā)過(guò)程,提高了質(zhì)量以及系統(tǒng)結(jié)構(gòu)的清晰性,并由于保證了 View/Model 的分離,使得一個(gè) Model 可以對(duì)于多種顯示形式的 View ,需要的僅僅是去改變 View 和 Controller 。
按照 MVC 思想,最容易想到的實(shí)現(xiàn)方案莫過(guò)于 jsp+servlet+javabean ,在這里面 jsp 對(duì)應(yīng)著 View , servlet 對(duì)應(yīng)著 Controller , javabean 對(duì)應(yīng)著 Model ,因?yàn)椴捎?servlet 可使用 servlet container 已經(jīng)封裝好的頁(yè)面數(shù)據(jù)請(qǐng)求對(duì)象 HttpServletRequest ,這樣就省去了自己封裝頁(yè)面請(qǐng)求數(shù)據(jù)的工作,作為 Controller 同時(shí)還需要承擔(dān)根據(jù)請(qǐng)求調(diào)用對(duì)應(yīng)的 javabean ,最簡(jiǎn)單的做法無(wú)非就是在 Servlet 中直接根據(jù)某種邏輯 ( 諸如反射或接口 ) 調(diào)用相應(yīng)的 bean 進(jìn)行執(zhí)行,之后將 HttpServletRequest 、 HttpServletResponse 作為參數(shù)傳入 javabean 進(jìn)行處理, javabean 從 HttpServletRequest 中獲取請(qǐng)求數(shù)據(jù),將返回的結(jié)果數(shù)據(jù)放入 HttpServletResponse ,整個(gè)過(guò)程結(jié)束后繼續(xù)由 Controller 接手進(jìn)行處理,這個(gè)時(shí)候作為 Controller 的 servlet 將根據(jù)處理的結(jié)果返回相應(yīng)的頁(yè)面,在這個(gè)模型使用時(shí)人們慢慢的發(fā)現(xiàn)了一個(gè)問(wèn)題,那就是隨著 jsp 、 javabean 的變化造成了 controller 的不斷修改,需要修改其中調(diào)用相應(yīng) javabean 以及轉(zhuǎn)發(fā)相應(yīng)頁(yè)面的部分,為了解決這個(gè)問(wèn)題,首先想到的是應(yīng)該分離根據(jù)請(qǐng)求調(diào)用相應(yīng) javabean 的步驟,這個(gè)時(shí)候采用了設(shè)計(jì)模式中的 front controller+application controller 的方法, front controller 負(fù)責(zé)接受頁(yè)面請(qǐng)求并進(jìn)行封裝,同時(shí)將此數(shù)據(jù)對(duì)象傳遞至 application controller ,由 application controller 來(lái)負(fù)責(zé)調(diào)用相應(yīng)的 bean ,這樣的設(shè)計(jì)其實(shí)都是遵循著一個(gè)設(shè)計(jì)原則,就是職責(zé)單一,通常實(shí)現(xiàn) application controller 的模式是 Command 模式,在這種情況下 MVC Framework 的結(jié)構(gòu)體系就演變成了 view+controller(front+application)+model 。
在完成了上述演變后慢慢又發(fā)現(xiàn)了一個(gè)問(wèn)題,就是 model 依賴于了 httpservletrequest ,這樣造成的一個(gè)問(wèn)題就是沒(méi)法測(cè)試,仍然要不斷重啟服務(wù)器來(lái)測(cè)試,當(dāng)然與此同時(shí)的發(fā)展是 model 層的細(xì)化,細(xì)化成用于響應(yīng)頁(yè)面請(qǐng)求的 action Layer+Domain Model Layer+Persistent Layer ,在這里不去討論后面層次的問(wèn)題,因?yàn)樽鳛?MVC Framework 它并不管你 Model 層是怎么個(gè)處理流程的。
慢慢也發(fā)現(xiàn)了另外一個(gè)問(wèn)題,那就是變化經(jīng)常要影響到 controller 的修改,于是便引入了采用配置文件的解決方法,編寫 action 的配置文件,在配置文件中控制根據(jù) action 的返回結(jié)果轉(zhuǎn)入相應(yīng)的 View ,這樣的話在將來(lái)需要改變的時(shí)候只需要去改變這個(gè)配置文件就可以了,保證了 Controller 的穩(wěn)定,這是典型的設(shè)計(jì)中的重點(diǎn)考慮因素,分離變化和不變化的,讓變化造成的影響最小。
但在引入了上面的配置文件后,慢慢又發(fā)現(xiàn)了問(wèn)題,那就是手寫配置文件總是容易出各種各樣的問(wèn)題,這個(gè)時(shí)候采用圖形化的界面來(lái)生成配置文件的想法又有了,這也就造就了 page flow 的誕生,當(dāng)然,這只是 page flow 的一小部分功能。
當(dāng)然,隨著MVC的發(fā)展,也帶動(dòng)了其他相關(guān)技術(shù)的發(fā)展,如異步請(qǐng)求/響應(yīng)模式(ajax、amowa,^_^)等。
在 MVC 思想接受后開源界的 MVC Framework 也是如雨后春筍般的冒出,比較知名的有 struts 、 webwork 、 spring mvc 等,這些 MVC Framework 基本都已經(jīng)做到了上面提及的 MVC 思想演變的一些需求,當(dāng)然,即使現(xiàn)在的 MVC Framework 是做到了,但在使用這些 MVC Framework 的時(shí)候我們通常又開始違背 MVC 思想的基本要素,就是保持 View 僅僅是 View 的原則,所以我比較推薦在 View 使用 Velocity 這之類的東西作為 View ,盡量保持 View 的純潔性,任何技術(shù)的發(fā)展都是循序漸進(jìn)的,不站在那個(gè)高度的時(shí)候是不知道前面還有什么樣的高山的,那么現(xiàn)在我們?nèi)鄙俚挠质鞘裁茨兀楷F(xiàn)在的 MVC Framework 中還存在著什么不足呢?這是值得我們思考的。
Frank W. Zametti有一篇文章,向我們展示了對(duì)Ajax(Asynchronous Javascript+XML)技術(shù)的應(yīng)用,尤其是Struts的應(yīng)用。他認(rèn)為在應(yīng)用Ajax中有很多有趣的事情,包括Ajax怎樣工作,為什么Ajax會(huì)被用到等等。
文章地址:
http://www.omnytex.com/articles/xhrstruts/
產(chǎn)生驗(yàn)證碼圖片的文件-----image.jsp
<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
<%!
Color getRandColor(int fc,int bc){//給定范圍獲得隨機(jī)顏色
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
%>
<%
//設(shè)置頁(yè)面不緩存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
// 在內(nèi)存中創(chuàng)建圖象
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 獲取圖形上下文
Graphics g = image.getGraphics();
//生成隨機(jī)類
Random random = new Random();
// 設(shè)定背景色
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
//設(shè)定字體
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
//畫邊框
//g.setColor(new Color());
//g.drawRect(0,0,width-1,height-1);
// 隨機(jī)產(chǎn)生155條干擾線,使圖象中的認(rèn)證碼不易被其它程序探測(cè)到
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x,y,x+xl,y+yl);
}
// 取隨機(jī)產(chǎn)生的認(rèn)證碼(4位數(shù)字)
String sRand="";
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10));
sRand+=rand;
// 將認(rèn)證碼顯示到圖象中
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));//調(diào)用函數(shù)出來(lái)的顏色相同,可能是因?yàn)榉N子太接近,所以只能直接生成
g.drawString(rand,13*i+6,16);
}
// 將認(rèn)證碼存入SESSION
session.setAttribute("rand",sRand);
// 圖象生效
g.dispose();
// 輸出圖象到頁(yè)面
ImageIO.write(image, "JPEG", response.getOutputStream());
%>
---------------使用驗(yàn)證碼圖片的文件---------a.jsp------------------------------------
<%@ page contentType="text/html;charset=gb2312" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>認(rèn)證碼輸入頁(yè)面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>
<body>
<form method=post action="check.jsp">
<table>
<tr>
<td align=left>系統(tǒng)產(chǎn)生的認(rèn)證碼:</td>
<td><img border=0 src="image.jsp"></td>
</tr>
<tr>
<td align=left>輸入上面的認(rèn)證碼:</td>
<td><input type=text name=rand maxlength=4 value=""></td>
</tr>
<tr>
<td colspan=2 align=center><input type=submit value="提交檢測(cè)"></td>
</tr>
</form>
</body>
</html>
-----------------驗(yàn)證的頁(yè)面----------check.jsp
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
<html>
<head>
<title>認(rèn)證碼驗(yàn)證頁(yè)面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>
<body>
<%
String rand = (String)session.getAttribute("rand");
String input = request.getParameter("rand");
%>
系統(tǒng)產(chǎn)生的認(rèn)證碼為: <%= rand %><br>
您輸入的認(rèn)證碼為: <%= input %><br>
<br>
<%
if (rand.equals(input)) {
%>
<font color=green>輸入相同,認(rèn)證成功!</font>
<%
} else {
%>
<font color=red>輸入不同,認(rèn)證失敗!</font>
<%
}
%>
</body>
</html>
JSF在很大程度上類似Struts,而不是類似Tapestry,可以說(shuō)是一種Struts 2.0,都是采取標(biāo)簽庫(kù)+組件的形式,只是JSF的組件概念沒(méi)有象Struts那樣必須繼承ActionForm的限制;JSF在事件粒度上要細(xì)膩,不象Struts那樣,一個(gè)表單一個(gè)事件,JSF可以細(xì)化到表單中的每個(gè)字段上。
JSF只有在組件和事件機(jī)制這個(gè)概念上類似Tapestry,但是不似Tapestry那樣是一個(gè)完全組件的框架,所以,如果你做一個(gè)對(duì)頁(yè)面要求靈活度相當(dāng)高的系統(tǒng),選用Tapestry是第一考慮。
Struts/JSF則適合在一般的數(shù)據(jù)頁(yè)面錄入的系統(tǒng)中,對(duì)于Struts和JSF的選用,我目前個(gè)人觀點(diǎn)是:如果你是一個(gè)新的系統(tǒng),可以直接從JSF開始;如果你已經(jīng)使用Struts,不必轉(zhuǎn)換,如果需要切換,可以將JSF和Tapestry一起考慮。
另外,JSF/Tapestry不只是支持Html,也支持多種客戶端語(yǔ)言如WML或XUI等。
這三者之間關(guān)系:如果說(shuō)Struts是左派;那Tapestry則是右派;而JSF則是中間派,中庸主義是SUN聯(lián)盟的一貫策略。
當(dāng)然,你也可以發(fā)表你在實(shí)踐中這三者任何一個(gè)的使用感受,以使得后來(lái)者有一個(gè)比較。
我們通過(guò)下表來(lái)比較這 三種框架在實(shí)現(xiàn)上圖各個(gè)功能時(shí)技術(shù)細(xì)節(jié),從而得出他們的異同點(diǎn)和偏重點(diǎn)。
|
Struts |
Tapestry3.0 |
JSF |
在View顯示的組件要求 |
組件必須繼承ActionForm |
分顯式調(diào)用和隱式調(diào)用 組件必須繼承BaseComponent |
普通POJO 無(wú)需繼承 Managed Bean |
組件在View顯示粒度 |
View頁(yè)面只能顯示與表單對(duì)應(yīng)的ActionForm,配置中Action ActionForm 頁(yè)面一般只能1:1:1關(guān)系。 |
可將組件嵌入頁(yè)面任何一行,對(duì)使用組件數(shù)量無(wú)限制。 |
同Tapestry |
頁(yè)面分區(qū)tiles |
使用Tiles標(biāo)簽庫(kù)實(shí)現(xiàn),需要另外tiles-def.xml配置文件 |
組件有自己的視圖頁(yè)面,通過(guò)調(diào)用組件即直接實(shí)現(xiàn)多個(gè)頁(yè)面組合。強(qiáng)大自然的頁(yè)面組合是其特點(diǎn)。 |
通過(guò)組件+標(biāo)簽庫(kù)實(shí)現(xiàn)Subview,但如需重用Layout,還要結(jié)合Tiles. |
頁(yè)面跳轉(zhuǎn) |
使用標(biāo)簽庫(kù)html:link中寫明目標(biāo)URL,URL名稱需要對(duì)照配置文件的path命名,與組件Action耦合。 |
URL名稱是目標(biāo)的組件名稱,不涉及URL和路徑等操作,方便穩(wěn)固。 |
類似Struts,也需要在配置文件中查找,與組件分離。 |
參數(shù)傳遞 |
使用html:link時(shí)傳遞參數(shù)超過(guò)一個(gè)以上處理麻煩。 |
直接調(diào)用組件,直接賦予參數(shù),沒(méi)有參數(shù)個(gè)數(shù)限制 |
參數(shù)分離傳遞給組件 |
事件觸發(fā) |
通過(guò)表單提交submit激活,不能細(xì)化到表單里字段。 |
能夠給于表單每個(gè)字段貼一個(gè)事件,事件組件必須實(shí)現(xiàn)PageListener接口 |
同Tapestry,事件組件必須實(shí)習(xí)ActionListener 接口 | |
Struts和JSF/Tapestry都屬于表現(xiàn)層框架,這兩種分屬不同性質(zhì)的框架,后者是一種事件驅(qū)動(dòng)型的組件模型,而Struts只是單純的MVC模式框架,老外總是急吼吼說(shuō)事件驅(qū)動(dòng)型就比MVC模式框架好,何以見得,我們下面進(jìn)行詳細(xì)分析比較一下到底是怎么回事?
首先事件是指從客戶端頁(yè)面(瀏覽器)由用戶操作觸發(fā)的事件,Struts使用Action來(lái)接受瀏覽器表單提交的事件,這里使用了Command模式,每個(gè)繼承Action的子類都必須實(shí)現(xiàn)一個(gè)方法execute。
在struts中,實(shí)際是一個(gè)表單Form對(duì)應(yīng)一個(gè)Action類(或DispatchAction),換一句話說(shuō):在Struts中實(shí)際是一個(gè)表單只能對(duì)應(yīng)一個(gè)事件,struts這種事件方式稱為application event,application event和component event相比是一種粗粒度的事件。
struts重要的表單對(duì)象ActionForm是一種對(duì)象,它代表了一種應(yīng)用,這個(gè)對(duì)象中至少包含幾個(gè)字段,這些字段是Jsp頁(yè)面表單中的input字段,因?yàn)橐粋€(gè)表單對(duì)應(yīng)一個(gè)事件,所以,當(dāng)我們需要將事件粒度細(xì)化到表單中這些字段時(shí),也就是說(shuō),一個(gè)字段對(duì)應(yīng)一個(gè)事件時(shí),單純使用Struts就不太可能,當(dāng)然通過(guò)結(jié)合JavaScript也是可以轉(zhuǎn)彎實(shí)現(xiàn)的。
而這種情況使用JSF就可以方便實(shí)現(xiàn),
<h:inputText id="userId" value="#{login.userId}"> <f:valueChangeListener type="logindemo.UserLoginChanged" /> </h:inputText> |
#{login.userId}表示從名為login的JavaBean的getUserId獲得的結(jié)果,這個(gè)功能使用struts也可以實(shí)現(xiàn),name="login" property="userId"
關(guān)鍵是第二行,這里表示如果userId的值改變并且確定提交后,將觸發(fā)調(diào)用類UserLoginChanged的processValueChanged(...)方法。
JSF可以為組件提供兩種事件:Value Changed和 Action. 前者我們已經(jīng)在上節(jié)見識(shí)過(guò)用處,后者就相當(dāng)于struts中表單提交Action機(jī)制,它的JSF寫法如下:
<h:commandButton id="login" commandName="login"> <f:actionListener type=”logindemo.LoginActionListener” /> </h:commandButton> |
從代碼可以看出,這兩種事件是通過(guò)Listerner這樣觀察者模式貼在具體組件字段上的,而Struts此類事件是原始的一種表單提交Submit觸發(fā)機(jī)制。如果說(shuō)前者比較語(yǔ)言化(編程語(yǔ)言習(xí)慣做法類似Swing編程);后者是屬于WEB化,因?yàn)樗莵?lái)自Html表單,如果你起步是從Perl/PHP開始,反而容易接受Struts這種風(fēng)格。
基本配置
Struts和JSF都是一種框架,JSF必須需要兩種包JSF核心包、JSTL包(標(biāo)簽庫(kù)),此外,JSF還將使用到Apache項(xiàng)目的一些commons包,這些Apache包只要部署在你的服務(wù)器中既可。
JSF包下載地址:http://java.sun.com/j2ee/javaserverfaces/download.html選擇其中Reference Implementation。
JSTL包下載在http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi
所以,從JSF的驅(qū)動(dòng)包組成看,其開源基因也占據(jù)很大的比重,JSF是一個(gè)SUN伙伴們工業(yè)標(biāo)準(zhǔn)和開源之間的一個(gè)混血兒。
上述兩個(gè)地址下載的jar合并在一起就是JSF所需要的全部驅(qū)動(dòng)包了。與Struts的驅(qū)動(dòng)包一樣,這些驅(qū)動(dòng)包必須位于Web項(xiàng)目的WEB-INF/lib,和Struts一樣的是也必須在web.xml中有如下配置:
<web-app> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping> </web-app> |
這里和Struts的web.xml配置何其相似,簡(jiǎn)直一模一樣。
正如Struts的struts-config.xml一樣,JSF也有類似的faces-config.xml配置文件:
<faces-config> <navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-outcome>login</from-outcome> <to-view-id>/welcome.jsp</to-view-id> </navigation-case> </navigation-rule>
<managed-bean> <managed-bean-name>user</managed-bean-name> <managed-bean-class>com.corejsf.UserBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config>
|
在Struts-config.xml中有ActionForm Action以及Jsp之間的流程關(guān)系,在faces-config.xml中,也有這樣的流程,我們具體解釋一下Navigation:
在index.jsp中有一個(gè)事件:
<h:commandButton label="Login" action="login" />
action的值必須匹配form-outcome值,上述Navigation配置表示:如果在index.jsp中有一個(gè)login事件,那么事件觸發(fā)后下一個(gè)頁(yè)面將是welcome.jsp
JSF有一個(gè)獨(dú)立的事件發(fā)生和頁(yè)面導(dǎo)航的流程安排,這個(gè)思路比struts要非常清晰。
managed-bean類似Struts的ActionForm,正如可以在struts-config.xml中定義ActionForm的scope一樣,這里也定義了managed-bean的scope為session。
但是如果你只以為JSF的managed-bean就這點(diǎn)功能就錯(cuò)了,JSF融入了新的Ioc模式/依賴性注射等技術(shù)。
Ioc模式
對(duì)于Userbean這樣一個(gè)managed-bean,其代碼如下:
public class UserBean {
private String name;
private String password;
// PROPERTY: name
public String getName() { return name; }
public void setName(String newValue) { name = newValue; }
// PROPERTY: password
public String getPassword() { return password; }
public void setPassword(String newValue) { password = newValue; }
}
<managed-bean> <managed-bean-name>user</managed-bean-name> <managed-bean-class>com.corejsf.UserBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope>
<managed-property> <property-name>name</property-name> <value>me</value> </managed-property>
<managed-property> <property-name>password</property-name> <value>secret</value> </managed-property> </managed-bean> |
faces-config.xml這段配置其實(shí)是將"me"賦值給name,將secret賦值給password,這是采取Ioc模式中的Setter注射方式。
Backing Beans
對(duì)于一個(gè)web form,我們可以使用一個(gè)bean包含其涉及的所有組件,這個(gè)bean就稱為Backing Bean, Backing Bean的優(yōu)點(diǎn)是:一個(gè)單個(gè)類可以封裝相關(guān)一系列功能的數(shù)據(jù)和邏輯。
說(shuō)白了,就是一個(gè)Javabean里包含其他Javabean,互相調(diào)用,屬于Facade模式或Adapter模式。
對(duì)于一個(gè)Backing Beans來(lái)說(shuō),其中包含了幾個(gè)managed-bean,managed-bean一定是有scope的,那么這其中的幾個(gè)managed-beans如何配置它們的scope呢?
<managed-bean> ... <managed-property> <property-name>visit</property-name> <value>#{sessionScope.visit}</value> </managed-property>
|
這里配置了一個(gè)Backing Beans中有一個(gè)setVisit方法,將這個(gè)visit賦值為session中的visit,這樣以后在程序中我們只管訪問(wèn)visit對(duì)象,從中獲取我們希望的數(shù)據(jù)(如用戶登陸注冊(cè)信息),而visit是保存在session還是application或request只需要配置既可。
UI界面
JSF和Struts一樣,除了JavaBeans類之外,還有頁(yè)面表現(xiàn)元素,都是是使用標(biāo)簽完成的,Struts也提供了struts-faces.tld標(biāo)簽庫(kù)向JSF過(guò)渡。
使用Struts標(biāo)簽庫(kù)編程復(fù)雜頁(yè)面時(shí),一個(gè)最大問(wèn)題是會(huì)大量使用logic標(biāo)簽,這個(gè)logic如同if語(yǔ)句,一旦寫起來(lái),搞的JSP頁(yè)面象俄羅斯方塊一樣,但是使用JSF標(biāo)簽就簡(jiǎn)潔優(yōu)美:
<jia:navigatorItem name="inbox" label="InBox" icon="/images/inbox.gif" action="inbox" disabled="#{!authenticationBean.inboxAuthorized}"/>
|
如果authenticationBean中inboxAuthorized返回是假,那么這一行標(biāo)簽就不用顯示,多干凈利索!
先寫到這里,我會(huì)繼續(xù)對(duì)JSF深入比較下去,如果研究過(guò)Jdon框架的人,可能會(huì)發(fā)現(xiàn),Jdon框架的jdonframework.xml中service配置和managed-bean一樣都使用了依賴注射,看來(lái)對(duì)Javabean的依賴注射已經(jīng)迅速地成為一種新技術(shù)象征,如果你還不了解Ioc模式,趕緊補(bǔ)課。
附Jsf核心教程一個(gè)JSF案例:login.rar