第三章 一個輕量級的Project Management Tool(LPMT)
在這里我們引入一個輕量級的 Project Management Tool(LPMT) 的例子,以此利用 MVC 設(shè)計模式和 Struts 技術(shù)來構(gòu)建一個結(jié)構(gòu)清晰、可擴展、可復(fù)用的 Web 應(yīng)用程序,并借此說明如何使用 Struts 來實現(xiàn) MVC 設(shè)計模式,以及在 Struts 的使用過程中如何構(gòu)建清晰的系統(tǒng)架構(gòu)以及實現(xiàn) WebForm 和代碼復(fù)用。這里我們假定一個 Project 、服務(wù)或者產(chǎn)品就是一個 Issue ;在每個 Issue 的開發(fā)周期內(nèi),當(dāng) Issue 在各個開發(fā)小組中流轉(zhuǎn)的時候會生成一個或多個任務(wù)稱為 IssueData ;每個 IssueData 都有相應(yīng)的開始時間、結(jié)束時間和提交時間; IssueData 的結(jié)束時間和提交時間是可更改的,每次更改系統(tǒng)自動生成一個 Log 記錄這種更改。 LPMT 需要實現(xiàn)對上述 Issue 、 IssueData 、 Log 的顯示、管理和其他必要的功能。本人所在的三人小組將以 Java 、 Hibernate 和 Struts Framework 技術(shù)為依托實現(xiàn)上述需求。
3.1 LPMT的用戶需求
我們通過采用與用戶面談的方式,初步抽象了以下的用戶需求 ( 系統(tǒng)功能 ) :
標(biāo)識
|
?????????????????????????
功能
|
分類
|
Y1.1 |
顯示
issue
的啟動時間,完成時間和工作組。
|
明顯
|
Y1.2 |
TL
可設(shè)定
issue
完成時間并公布。
|
明顯
|
Y1.3 |
TL 可修改原定完成時間。(注:需注明原因) |
明顯
|
Y1.4 |
issue 進度修改后自動給相關(guān) TL 發(fā) e-mail 。 |
明顯
|
Y1.5 |
可記錄 issue 原狀態(tài)及修改后狀態(tài)。 |
明顯
|
Y1.6 |
可查詢 issue 目前進度或變更情況。 |
明顯
|
Y1.7 |
可統(tǒng)計 issue 的各項情況(如從完成到啟動的時間間隔等)并生 報表。 |
明顯
|
Y1.8 |
各部門完成各自進度后系統(tǒng)自動給相關(guān)人員發(fā) e-mail 。 |
明顯
|
Y1.9 |
PM 可設(shè)置或調(diào)整 issue 的優(yōu)先級。(手動) |
明顯
|
由于用戶的需求是模糊不確定、可擴展的,故由此構(gòu)建的系統(tǒng)必須滿足結(jié)構(gòu)清晰、可復(fù)用、可擴展等要求。
3.2 LPMT的需求實現(xiàn)
通過架構(gòu)設(shè)計和編碼,我們最終實現(xiàn)了上述大部分需求。
?
?
?
第四章 LPMT中的MVC多層架構(gòu)實現(xiàn)
在 LPMT 中,為了使得系統(tǒng)可擴展、易維護、易修改、結(jié)構(gòu)清晰,我們采用 MVC 設(shè)計模式,構(gòu)建多層的體系結(jié)構(gòu),通過各層的協(xié)調(diào)配合,實現(xiàn)系統(tǒng)需求。
4.1 系統(tǒng)架構(gòu)
在 LPMT 中,我們設(shè)置了 View 、 Controller 、 Logic 、 Model 、 Data Persistence 五個邏輯層,通過各邏輯層之間的配合,應(yīng)用 MVC 設(shè)計模式,實現(xiàn)系統(tǒng)需求。
也稱數(shù)據(jù)持久層。數(shù)據(jù)持久層負責(zé)數(shù)據(jù)庫映射,隔離數(shù)據(jù)庫操作,將數(shù)據(jù)庫操作設(shè)計成 Java APIs 。此邏輯層可由 Hibernate 、 DAO 、 Connection Pool 等多種技術(shù)實現(xiàn)。在 LPMT 中,我們采用 Hibernate 技術(shù),將數(shù)據(jù)庫的表映射成為對象 (issuecontrol.objects 包 ) ,比如 Issue 表對應(yīng) Issue.java 、 Flag 表對應(yīng) Flag.java 、 IssueData 表對應(yīng) IssueData.java 等,將對數(shù)據(jù)庫的各種操作封裝在 IssueDAO 和 UserDAO 兩個 DAO 類中。 DAO 類代碼見附錄。
也稱事務(wù)邏輯層。在一個規(guī)范的 J2EE 架構(gòu)中,不同層的數(shù)據(jù)表示應(yīng)該被限制在層內(nèi),而不應(yīng)該擴散到其它層,這樣可以降低層間的耦合性,提高 J2EE 架構(gòu)整體的可維護性和可擴展性。比如說 View 層的邏輯進行了修改,那么只需要修改 ActionForm 的結(jié)構(gòu),而不需要觸動 Data Persistence 層和 Logic 層的代碼修改。同樣,當(dāng)數(shù)據(jù)庫表進行了小的調(diào)整,那么也只需要修改 Data Persistence 層數(shù)據(jù)表示,而不需要觸動 Model 層代碼和 View 層代碼。因此,我們在 Model 層和 Data Persistence 層中間插入 Logic 層,以降低 Data Persistence 和 Model 層之間的耦合關(guān)系。 Logic 層類以 Bean 結(jié)尾,如 IssueBean.java 、 UserBean.java 。 Model 通過 Logic Bean 調(diào)用 Data Persistence 層的 DAO 類,實現(xiàn)對數(shù)據(jù)庫操作以及其他業(yè)務(wù)邏輯操作。 Bean 類代碼見附錄。
也稱對象層。包括所有 Action 類、 ActionForm 類、和其他顯示類 (issuecontrol.actionform 包、 issuecontrol.action 包、 issuecontrol.view 包 ) 。
issuecontrol.actionform 包中的所有類都派生自 ActionForm ,與 View 層的表單頁面一一對應(yīng),用于定義客戶端顯示表單,封裝業(yè)務(wù)數(shù)據(jù),在 Logic 、 Model 、 Controller 、 View 各邏輯層之間作為數(shù)據(jù)傳輸媒介。在 J2EE 架構(gòu)里面, ActionForm 可以由 Entity Bean 和 Session Bean 來表示,以期實現(xiàn)業(yè)務(wù)邏輯重用。
Issuecontrol.view 包為顯示包,其中的類為顯示類,負責(zé)特殊對象表單的顯示,比如分頁顯示的 Issue 等。
Issuecontrol.action 包中的所有類都派生自 Action ,用于封裝具體的處理邏輯,調(diào)用 Logic 層的業(yè)務(wù)邏輯類,實現(xiàn)業(yè)務(wù)操作,讀寫 ActionForm 類,并將結(jié)果返回 View 層顯示。部分 Action 和 ActionForm 見附錄。
也稱控制層。包括 ActionServlet 、 ActionMapping 、 RequestProcessor 等 Struts 類。 ActionServlet 負責(zé)接受用戶請求,并將用戶請求引導(dǎo)到正確的頁面。 ActionMapping 包含 ActionServlet 的目標(biāo)映射, RequestProcessor 負責(zé)與 Action 交互。 ActionMapping 的目標(biāo)映射在 struts-config.xml 配置文件中完成。 Struts-config.xml 代碼見附錄。
也稱顯示層。由 JSP 頁面組成。每個 JSP 頁面由 HTML 和 Struts 標(biāo)簽庫實現(xiàn)控制和顯示邏輯。部分 View 代碼見附錄。
4.2 MVC模式的實現(xiàn)邏輯
上述各邏輯層各司其職,互相配合,盡量降低邏輯層之間的耦合性,提高內(nèi)聚性。圖 11 為上述邏輯層之間的關(guān)系。
(1)???? 客戶端向服務(wù)器提交 Http 請求
(2)???? ActionServlet 接受客戶端提交的 Http 請求,載入屬性文件 (Properties files) ,選擇目標(biāo) Action ,將控制權(quán)交給 RequestProcessor 。
(3)???? RequestProcessor 根據(jù) URL 和 struts-config.xml 中的 actionmapping 尋找相應(yīng)的 Action ;新建或者復(fù)用對應(yīng)的 ActionForm ,封裝包含在請求信息中的表單屬性,檢查數(shù)據(jù)的合法性,并將 ActionFor 傳遞給目標(biāo) Action 。
(4)???? 目標(biāo) Action 接受傳遞過來的 ActionForm ,讀出 ActionForm 里面的屬性,調(diào)用 Logic 層的業(yè)務(wù)邏輯 Bean ,而 Bean 則調(diào)用相應(yīng) DAO 類的方法,進行持久對象的持久化操作,最終完成相應(yīng)的業(yè)務(wù)邏輯操作。操作過程一旦出現(xiàn)異常, Action 也將一并處理。在完成業(yè)務(wù)邏輯操作后, Action 返回一個 ActionMapping 對象,告訴 RequestProcessor 指向目標(biāo) View 頁面。
(5)???? View 頁面利用既定接口,將結(jié)果顯示給客戶端。
4.3 分頁顯示Issue的MVC模式實現(xiàn)
下面以分頁顯示所有 Issue 為例,說明上述各邏輯層之間是如何配合以完成既定操作。
(1)???? 客戶端向服務(wù)器提交 URL 請求: http://localhost:8000/issuecontrol/issueAction.do
(2)???? ActionServlet 通過 RequestProcessor 將請求提交到 IssueAction
(3)???? Action 判斷此次的請求類型為顯示,參數(shù) action = “view” ;通過 HttpServletRequest.getParameter 方法取得當(dāng)前頁碼 viewPage ;假如 viewPage 為空,則當(dāng)前頁碼為 1 ;設(shè)定每頁顯示的行數(shù) pageSize = 8 ;調(diào)用 IssueBean.getIssueByPage(new Page(pageNum,pageSize)) 方法,將結(jié)果封裝到一個 PageView 對象 selectPageView 中;調(diào)用 httpServletRequest.setAttribute 方法將結(jié)果放到 HttpServletRequest 中;返回一個 ActionMapping 對象,內(nèi)包含目標(biāo)映射 viewIssue 指向 IssueList.jsp ,將控制權(quán)交還給 RequestProcessor 。 IssueBean.getIssueByPage 方法是通過調(diào)用 IssueDAO.getIssueByPage 方法完成其業(yè)務(wù)邏輯的。
(4)???? RequestProcessor 將頁面導(dǎo)向 IssueList.jsp 。
(5)???? IssueList.jsp 將結(jié)果顯示在客戶端。
Action 中的代碼塊示例如下:
??? String viewPage=httpServletRequest.getParameter("viewPage");
??? int pageNum=1;
??? String address = "viewIssue";
??? // 查看所有的 Issue
??? if(("view".equals(action)) || (action == null)) {
????? address = "viewIssue";
????? if((viewPage!= null)&&(viewPage.length()!= 0)){
??????? pageNum = Integer.parseInt(viewPage);
????? }
????? int pageSize=8;
????? //1. 先進行參數(shù)分析
????? //2. 下面調(diào)用邏輯層方法得到顯示的對象
????? PageView selectPageView=IssueBean.getIssueByPage(new Page(pageNum,pageSize));
????? //3. 放到 request 中然后轉(zhuǎn)發(fā)
????? httpServletRequest.setAttribute("items",selectPageView.getItems());
????? httpServletRequest.setAttribute("selectPageView",selectPageView);
????? httpServletRequest.setAttribute("action",action);
}