第七章 LPMT中的代碼復用技術
軟件復用是將已有的軟件及其有效成分用于構造新的軟件或系統。它不僅是對軟件程序的復用,還包括對軟件生產過程中其它勞動成果的復用,如項目計劃書、可行性報告、需求分析、概要設計、詳細設計、編碼 ( 源程序 ) 、測試用例、文檔與使用手冊等等。面對越來越復雜的業務邏輯,軟件復用技術一直以來都是業界關注的焦點,相關理論和技術層出不窮。作為一種比較出色的實現 MVC 設計模式的 FrameWork , Jakarta Struts 提供了一種比較理想的軟件復用方法。在 LPMT 中,我們對其中的代碼復用進行了有益的嘗試。
7.1 Model復用
這里的 Model 包括所有的 Action 類、 ActionForm 類。
在 LPMT 中,我們設計了兩個 Action : IssueAction 和 MainAction 。其中, IssueAction 負責 IssueAction 及 IssueData 、 ChangeLog 操作, MainAction 負責 User 及 Login 操作,據此實現 Action 復用。在每個 Action 中,通過參數 action 的值來判斷操作的類型,繼而實現相應的業務邏輯操作;
IssueAction :
// 查看所有的 Issue
?if(("view".equals(action)) || (action == null)) {
}
?// 查看單個 Issue 及其 IssueDatas 和 ChangeLogs
?else if("viewDetail".equals(action)) {
}
?// 新建一個 Issue
?else if("create".equals(action)) {
}
?// 編輯已有的 Issue
?else if("edit".equals(action)) {
}
?// 刪除對應的 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 :
??? // 判斷用戶是否取消操作 , 是則就轉向 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"))
??? {
??? }
??? // 判斷是否要刪除用戶
??? if(request.getParameter("action").equals("deleteUser"))
??? {
??? }
??? // 刪除用戶
??? if(request.getParameter("action").equals("doDeleteUser"))
??? {
??? }
??? // 查看用戶列表
??? if(request.getParameter("action").equals("viewUser"))
??? {
??? }
通過參數 address 來保留下一步的映射目標;在 struts-config.xml 配置文件中,我們通過 global-forward 和 action-forward 映射,將不同的 address 映射到相應的目標 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>
在必要的時候, IssueAction 和 MainAction 可以合為一個 Action ,由這個 Action 來負責所有業務邏輯操作。
在面向對象設計之后,對于每個需要顯示的實體對象,我們定義了一個 ActionForm ,如 IssueActionForm 、 ComponentActionForm 、 PriorityActionForm 、 TypeActionForm 、 LogActionForm 。 ActionForm 實現了 Serializable 接口,每個 xxxActionForm 繼承 ActionForm 類,適合用于數據封裝、顯示和遠程網絡傳播。
通過在 struts-config.xml 中的映射,一個 Action 可以對應多個 ActionForm ,實現 Action 和 ActionForm 復用。
? <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復用
這里的 View 主要是指用 Jakarta Struts 標簽庫構建起來的 JSP 頁面。正如本文“ LPMT 中的控制結構”和“ WebForm 的 Jakarta Struts 標簽技術實現”部分所說的,通過 <logic:equal> 標簽和 Action 、 ActionForm 中 action 、 actionType 參數的設置,可以很容易將創建、查看、修改、刪除等功能的 View 顯示集合在一個 JSP 頁面完成。理論上,可以只創建一個 View ,但是由此所帶來的創建、修改和維護成本將不合算。所以我們通常把相同或相似的業務邏輯結果顯示放在一個 View 中完成,以此實現 View 的復用。
7.3 Controller復用
Struts 的 Controller 由 ActionServlet 、 RequestProcessor 、 ActionMapping 等類組成。這些類在 Struts 中唯一充當控制器角色, Model 中相應的 ActionForm 和 URL 中的參數則作為數據封裝和傳遞的媒介。這些類通過相應的方法和接口復用,實現 Model 的復用。
7.4 其他面向對象的代碼復用
在 LPMT 中,我們采用了面向對象的分析、設計和編碼方法,因此,我們主要著眼于面向對象思想在下面幾個方面實現代碼復用:
在軟構件的定義中,用戶只關心事件的輸入輸出,對事件內部不必關心,方法和事件是獨立于應用的,用戶可以在軟構件中定義自己的事件,對于內部的復雜性調用這并不知曉,從而提高了隱蔽性。在 Logic 、 DataPersistence 等相關 Bean 和 DAO 類中,我們遵循數據封裝和持久原則,在 Action 中調用相關方法只要符合相應的輸入和輸出條件即可實現既定的業務邏輯操作 , 而不需要了解業務邏輯的處理過程。
重載就是在同一軟件構件中用同一名字來表示不同的方法名。一般有兩種實現方法,一是方法參數的個數重載,二是方法參數的類型重載。在 IssueDAO 中,根據應用需求,我們定義了 delOneLog 方法,通過方法參數的類型( Log 類型、 Long 類型)不同實現重載。另外的 getIssueByPage 方法,則通過方法參數的個數不同實現重載。詳細代碼參考附錄。
繼承就是高層的類在不同范圍的復用。在 Web 系統設計中,經常需要驗證客戶端的輸入是否符合要求,比如輸入不能為空、 Email 段輸入必須符合規范等等。在 Model 層的 ActionForm 類中,我們定義了一個高層類 BaseForm , BaseForm 擴展了 ActionForm ,定義了幾個常用的數據驗證方法,比如驗證輸入是否為空的 isBlankString 方法、驗證前后輸入是否一樣的 isTheSame 等。其他的 ActionForm 通過繼承 BaseForm 實現。另外一種方法,可以通過定義一個 ValidateForm 的靜態類來負責常用的幾種輸入驗證,其他 ActionForm 類在 Validate 方法中通過調用 ValidateForm 類的相應方法即可實現相應的輸入驗證邏輯。
通過上述背景闡述、技術分析和代碼實踐,可以看到,使用 Struts 構建的實現了 MVC 設計模式的系統,在提高系統的構建效率、可擴展性、可維護性、可復用性方面均有突出的表現。而本文在 LPMT 中的 Struts 應用方式比如架構設計、 WebForm 構建技術等有效的發揮了 Struts 技術的優勢,快速實現了系統的應用需求,有一定的參考價值。
在本文寫作和系統設計過程中,
(1) ??? James Goodwill《Mastering Jakarta Struts》Indianapolis, Indiana,Canada Wiley Publishing ,Inc.2002年
(2) ??? Hafech Mili,Ali Mili,Sherif Yacoub etal著,韓柯譯《基于重用的軟件工程》北京電子工業出版社 2003年
(3)
???
孫琪《軟件復用技術概述》http://www.tongtech.com/jsqy/yqxwview.asp?id=209?
(4) ??? 趙晨希 《用Struts建立MVC應用的介紹》 http://www-900.ibm.com/developerWorks/cn/java/l-struts-mvc/index.shtml ?2002 年 12 月
(5)
???
劉天北 《理解企業應用框架》http://www.socent.com/crm_oa/message.asp?id=118