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