第七章 LPMT中的代碼復(fù)用技術(shù)
軟件復(fù)用是將已有的軟件及其有效成分用于構(gòu)造新的軟件或系統(tǒng)。它不僅是對(duì)軟件程序的復(fù)用,還包括對(duì)軟件生產(chǎn)過程中其它勞動(dòng)成果的復(fù)用,如項(xiàng)目計(jì)劃書、可行性報(bào)告、需求分析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、編碼 ( 源程序 ) 、測(cè)試用例、文檔與使用手冊(cè)等等。面對(duì)越來越復(fù)雜的業(yè)務(wù)邏輯,軟件復(fù)用技術(shù)一直以來都是業(yè)界關(guān)注的焦點(diǎn),相關(guān)理論和技術(shù)層出不窮。作為一種比較出色的實(shí)現(xiàn) MVC 設(shè)計(jì)模式的 FrameWork , Jakarta Struts 提供了一種比較理想的軟件復(fù)用方法。在 LPMT 中,我們對(duì)其中的代碼復(fù)用進(jìn)行了有益的嘗試。
7.1 Model復(fù)用
這里的 Model 包括所有的 Action 類、 ActionForm 類。
在 LPMT 中,我們?cè)O(shè)計(jì)了兩個(gè) Action : IssueAction 和 MainAction 。其中, IssueAction 負(fù)責(zé) IssueAction 及 IssueData 、 ChangeLog 操作, MainAction 負(fù)責(zé) User 及 Login 操作,據(jù)此實(shí)現(xiàn) Action 復(fù)用。在每個(gè) Action 中,通過參數(shù) action 的值來判斷操作的類型,繼而實(shí)現(xiàn)相應(yīng)的業(yè)務(wù)邏輯操作;
IssueAction :
// 查看所有的 Issue
?if(("view".equals(action)) || (action == null)) {
}
?// 查看單個(gè) Issue 及其 IssueDatas 和 ChangeLogs
?else if("viewDetail".equals(action)) {
}
?// 新建一個(gè) Issue
?else if("create".equals(action)) {
}
?// 編輯已有的 Issue
?else if("edit".equals(action)) {
}
?// 刪除對(duì)應(yīng)的 Issue
?else if("delete".equals(action)) {
}
?// 保存新建的 Issue
?else if("save".equals(action)) {
}
?// 新建 IssueData
?else if("createIssueData".equals(action)) {
}
?// 保存新建的 IssueData
?else if("saveIssueData".equals(action)) {
}
?// 編輯 IssueData
?else if("editIssueData".equals(action)) {
}
?// 刪除 IssueData
?else if("deleteIssueData".equals(action)) {
}
MainAction :
??? // 判斷用戶是否取消操作 , 是則就轉(zhuǎn)向 main.jsp
??? if(this.isCancelled(request))
??? {
??? }
??? // 登陸
??? if(request.getParameter("action").equals("login"))
??? {
??? }
??? // 顯示添加新用戶界面
??? if(request.getParameter("action").equals("newUser"))
??? {
??? }
??? // 添加新用戶
?? ?if(request.getParameter("action").equals("doNewUser"))
??? {
??? }
??? // 顯示要修改的用戶的資料
??? if(request.getParameter("action").equals("updateUser"))
??? {
??? }
??? // 修改用戶資料
??? if(request.getParameter("action").equals("doUpdateUser"))
??? {
??? }
??? // 判斷是否要?jiǎng)h除用戶
??? if(request.getParameter("action").equals("deleteUser"))
??? {
??? }
??? // 刪除用戶
??? if(request.getParameter("action").equals("doDeleteUser"))
??? {
??? }
??? // 查看用戶列表
??? if(request.getParameter("action").equals("viewUser"))
??? {
??? }
通過參數(shù) address 來保留下一步的映射目標(biāo);在 struts-config.xml 配置文件中,我們通過 global-forward 和 action-forward 映射,將不同的 address 映射到相應(yīng)的目標(biāo) View 。
? ?? <global-forwards>
??? ????? <forward name="loginSuccess" path="/main.jsp" />
??? ????? <forward name="newUser" path="/newUser.jsp"/>
??? ????? <forward name="updateUser" path="/newUser.jsp"/>
??? ????? <forward name="deleteUser" path="/deleteUser.jsp"/>
??? ????? <forward name="login" path="/index.jsp"/>
??? ????? <forward name="success" path="/message.jsp"/>
?????? ?????? <forward name="operationSuccess" path="/Success.jsp" />
??? ????? <forward name="error" path="/error.jsp"/>
??? ????? <forward name="viewUser" path="/viewUser.jsp"/>
? ?? </global-forwards>
??? <action name="issueActionForm" type="issuecontrol.action.IssueAction" validate="false" scope="request" path="/issueAction">
????? ? <forward name="viewIssue" path="/IssueList.jsp" />
????? ? <forward name="viewIssueDetail" path="/IssueDetail.jsp" />
????? ? <forward name="issueData" path="/IssueData.jsp" />
</action>
在必要的時(shí)候, IssueAction 和 MainAction 可以合為一個(gè) Action ,由這個(gè) Action 來負(fù)責(zé)所有業(yè)務(wù)邏輯操作。
在面向?qū)ο笤O(shè)計(jì)之后,對(duì)于每個(gè)需要顯示的實(shí)體對(duì)象,我們定義了一個(gè) ActionForm ,如 IssueActionForm 、 ComponentActionForm 、 PriorityActionForm 、 TypeActionForm 、 LogActionForm 。 ActionForm 實(shí)現(xiàn)了 Serializable 接口,每個(gè) xxxActionForm 繼承 ActionForm 類,適合用于數(shù)據(jù)封裝、顯示和遠(yuǎn)程網(wǎng)絡(luò)傳播。
通過在 struts-config.xml 中的映射,一個(gè) Action 可以對(duì)應(yīng)多個(gè) ActionForm ,實(shí)現(xiàn) Action 和 ActionForm 復(fù)用。
? <form-beans>
??? <form-bean name="LoginForm" type="issuecontrol.actionform.LoginForm" />
??? <form-bean name="NewUserForm" type="issuecontrol.actionform.NewUserForm"/>
??? <form-bean name="BaseForm" type="issuecontrol.actionform.BaseForm"/>
?????? <form-bean name="loginActionForm" type="issuecontrol.actionform.LoginActionForm" />
??? <form-bean name="componentActionForm" type="issuecontrol.actionform.ComponentActionForm" />
??? <form-bean name="environmentActionForm" type="issuecontrol.actionform.EnvironmentActionForm" />
??? <form-bean name="flagActionForm" type="issuecontrol.actionform.FlagActionForm" />
??? <form-bean name="issueActionForm" type="issuecontrol.actionform.IssueActionForm" />
??? <form-bean name="logActionForm" type="issuecontrol.actionform.LogActionForm" />
??? <form-bean name="priorityActionForm" type="issuecontrol.actionform.PriorityActionForm" />
??? <form-bean name="typeActionForm" type="issuecontrol.actionform.TypeActionForm" />
??? <form-bean name="issueDataActionForm" type="issuecontrol.actionform.IssueDataActionForm"/>
? </form-beans>
? <action-mappings>
??? <action name="LoginForm" type="issuecontrol.action.MainAction" validate="true" scope="session" input="/index.jsp" path="/login" />
??? <action path="/newUser" name="BaseForm" type="issuecontrol.action.MainAction" validate="false" scope="request" input="/main.jsp"/>
??? <action path="/doNewUser" name="NewUserForm" type="issuecontrol.action.MainAction" validate="true" scope="request" input="/newUser.jsp"/>
??? <action path="/viewUser" name="BaseForm" type="issuecontrol.action.MainAction" validate="false" scope="request" input="/main.jsp"/>
??? <action path="/updateUser" name="BaseForm" type="issuecontrol.action.MainAction" validate="false" scope="request" input="/viewUser.jsp"/>
??? <action path="/doUpdateUser" name="NewUserForm" type="issuecontrol.action.MainAction" validate="true" scope="request" input="/newUser.jsp"/>
??? <action path="/deleteUser" name="BaseForm" type="issuecontrol.action.MainAction" validate="false" scope="request" input="/viewUser.jsp"/>
??? <action path="/doDeleteUser" name="BaseForm" type="issuecontrol.action.MainAction" validate="false" scope="request" input="/deleteUser.jsp"/>
??? <action name="issueActionForm" type="issuecontrol.action.IssueAction" validate="false" scope="request" path="/issueAction">
??? </action>
?????? <action name="issueDataActionForm" type="issuecontrol.action.IssueAction" validate="false" scope="request" path="/issueDataAction"/>
? </action-mappings>
7.2 View復(fù)用
這里的 View 主要是指用 Jakarta Struts 標(biāo)簽庫構(gòu)建起來的 JSP 頁面。正如本文“ LPMT 中的控制結(jié)構(gòu)”和“ WebForm 的 Jakarta Struts 標(biāo)簽技術(shù)實(shí)現(xiàn)”部分所說的,通過 <logic:equal> 標(biāo)簽和 Action 、 ActionForm 中 action 、 actionType 參數(shù)的設(shè)置,可以很容易將創(chuàng)建、查看、修改、刪除等功能的 View 顯示集合在一個(gè) JSP 頁面完成。理論上,可以只創(chuàng)建一個(gè) View ,但是由此所帶來的創(chuàng)建、修改和維護(hù)成本將不合算。所以我們通常把相同或相似的業(yè)務(wù)邏輯結(jié)果顯示放在一個(gè) View 中完成,以此實(shí)現(xiàn) View 的復(fù)用。
7.3 Controller復(fù)用
Struts 的 Controller 由 ActionServlet 、 RequestProcessor 、 ActionMapping 等類組成。這些類在 Struts 中唯一充當(dāng)控制器角色, Model 中相應(yīng)的 ActionForm 和 URL 中的參數(shù)則作為數(shù)據(jù)封裝和傳遞的媒介。這些類通過相應(yīng)的方法和接口復(fù)用,實(shí)現(xiàn) Model 的復(fù)用。
7.4 其他面向?qū)ο蟮拇a復(fù)用
在 LPMT 中,我們采用了面向?qū)ο蟮姆治觥⒃O(shè)計(jì)和編碼方法,因此,我們主要著眼于面向?qū)ο笏枷朐谙旅鎺讉€(gè)方面實(shí)現(xiàn)代碼復(fù)用:
在軟構(gòu)件的定義中,用戶只關(guān)心事件的輸入輸出,對(duì)事件內(nèi)部不必關(guān)心,方法和事件是獨(dú)立于應(yīng)用的,用戶可以在軟構(gòu)件中定義自己的事件,對(duì)于內(nèi)部的復(fù)雜性調(diào)用這并不知曉,從而提高了隱蔽性。在 Logic 、 DataPersistence 等相關(guān) Bean 和 DAO 類中,我們遵循數(shù)據(jù)封裝和持久原則,在 Action 中調(diào)用相關(guān)方法只要符合相應(yīng)的輸入和輸出條件即可實(shí)現(xiàn)既定的業(yè)務(wù)邏輯操作 , 而不需要了解業(yè)務(wù)邏輯的處理過程。
重載就是在同一軟件構(gòu)件中用同一名字來表示不同的方法名。一般有兩種實(shí)現(xiàn)方法,一是方法參數(shù)的個(gè)數(shù)重載,二是方法參數(shù)的類型重載。在 IssueDAO 中,根據(jù)應(yīng)用需求,我們定義了 delOneLog 方法,通過方法參數(shù)的類型( Log 類型、 Long 類型)不同實(shí)現(xiàn)重載。另外的 getIssueByPage 方法,則通過方法參數(shù)的個(gè)數(shù)不同實(shí)現(xiàn)重載。詳細(xì)代碼參考附錄。
繼承就是高層的類在不同范圍的復(fù)用。在 Web 系統(tǒng)設(shè)計(jì)中,經(jīng)常需要驗(yàn)證客戶端的輸入是否符合要求,比如輸入不能為空、 Email 段輸入必須符合規(guī)范等等。在 Model 層的 ActionForm 類中,我們定義了一個(gè)高層類 BaseForm , BaseForm 擴(kuò)展了 ActionForm ,定義了幾個(gè)常用的數(shù)據(jù)驗(yàn)證方法,比如驗(yàn)證輸入是否為空的 isBlankString 方法、驗(yàn)證前后輸入是否一樣的 isTheSame 等。其他的 ActionForm 通過繼承 BaseForm 實(shí)現(xiàn)。另外一種方法,可以通過定義一個(gè) ValidateForm 的靜態(tài)類來負(fù)責(zé)常用的幾種輸入驗(yàn)證,其他 ActionForm 類在 Validate 方法中通過調(diào)用 ValidateForm 類的相應(yīng)方法即可實(shí)現(xiàn)相應(yīng)的輸入驗(yàn)證邏輯。
通過上述背景闡述、技術(shù)分析和代碼實(shí)踐,可以看到,使用 Struts 構(gòu)建的實(shí)現(xiàn)了 MVC 設(shè)計(jì)模式的系統(tǒng),在提高系統(tǒng)的構(gòu)建效率、可擴(kuò)展性、可維護(hù)性、可復(fù)用性方面均有突出的表現(xiàn)。而本文在 LPMT 中的 Struts 應(yīng)用方式比如架構(gòu)設(shè)計(jì)、 WebForm 構(gòu)建技術(shù)等有效的發(fā)揮了 Struts 技術(shù)的優(yōu)勢(shì),快速實(shí)現(xiàn)了系統(tǒng)的應(yīng)用需求,有一定的參考價(jià)值。
在本文寫作和系統(tǒng)設(shè)計(jì)過程中,
(1) ??? James Goodwill《Mastering Jakarta Struts》Indianapolis, Indiana,Canada Wiley Publishing ,Inc.2002年
(2) ??? Hafech Mili,Ali Mili,Sherif Yacoub etal著,韓柯譯《基于重用的軟件工程》北京電子工業(yè)出版社 2003年
(3)
???
孫琪《軟件復(fù)用技術(shù)概述》http://www.tongtech.com/jsqy/yqxwview.asp?id=209?
(4) ??? 趙晨希 《用Struts建立MVC應(yīng)用的介紹》 http://www-900.ibm.com/developerWorks/cn/java/l-struts-mvc/index.shtml ?2002 年 12 月
(5)
???
劉天北 《理解企業(yè)應(yīng)用框架》http://www.socent.com/crm_oa/message.asp?id=118