posts - 15,  comments - 34,  trackbacks - 27
          在對一個J2EE項目的重構(gòu)、增加新功能的過程中,對客戶端GUI程序,我們使用了State模式。結(jié)果顯示,該模式的使用,不但減少了客戶端GUI程序的程序規(guī)模(LOC),而且,該部分的開發(fā)及單元測試時間大大減少,同時,在集成測試中發(fā)現(xiàn)的缺陷數(shù)量比使用該模式前平均減少了3倍。本文就該項目中使用State模式的方式進行介紹。

          引言
          在分層軟件體系結(jié)構(gòu)中,服務(wù)端程序關(guān)注于實現(xiàn)業(yè)務(wù)邏輯,客戶端程序則包含用戶界面。服務(wù)端程序由客戶端程序調(diào)用,其請求、響應(yīng)模式在設(shè)計時已經(jīng)確定,運行時出現(xiàn)問題的概率較小。相反,客戶端程序與用戶直接交互,雖然有正確規(guī)定的操作順序或模式,但是用戶的操作是不可預(yù)知的,程序必須處理各種操作錯誤、加上數(shù)據(jù)輸入有效驗證等要求,使得客戶端程序的開發(fā)成本上升。

          因而,一旦有經(jīng)過充分測試的、甚至是通過驗收的用戶交互程序GUI,應(yīng)該盡可能的重用該GUI,以提高軟件的可靠性、可維護性。

          在對一個J2EE項目的重構(gòu)、增加新功能的過程中,對客戶端GUI程序,我們使用了State模式。結(jié)果顯示,該模式的使用,不但減少了客戶端GUI程序的程序規(guī)模(LOC),而且,該部分的開發(fā)及單元測試時間大大減少,同時,在集成測試中發(fā)現(xiàn)的缺陷數(shù)量比使用該模式前平均減少了3倍。本文就該項目中使用State模式的方式進行介紹。

          1. State模式
          首先,先簡單介紹一下State模式。

          該模式是指在對象的內(nèi)部狀態(tài)改變時改變對象的行為【1】。其結(jié)構(gòu)如圖1所示。

          圖1 State模式結(jié)構(gòu)
          圖1 State模式結(jié)構(gòu)

          模式中各個參與者職責(zé)簡介如下:

          • Context:用戶對象,擁有一個State類型的成員,以標識對象的當(dāng)前狀態(tài);
          • State:接口或基類,封裝與Context的特定狀態(tài)相關(guān)的行為;
          • ConcreteState:接口實現(xiàn)類或子類,實現(xiàn)了一個與Context某個狀態(tài)相關(guān)的行為。

          運行時,Context將與狀態(tài)相關(guān)的請求委托給當(dāng)前的ConcreteState對象處理。關(guān)于State模式更詳盡的介紹,請參閱參考文獻1。

          2. 客戶端應(yīng)用
          本模式的目標是分離客戶端軟件中的變化部分與不變部分,以使得變化的部分可獨立于不變的部分,有利于擴充新的功能,也有利于維護。

          在項目中,對于客戶端GUI的重用有兩種方式。

          • 方式1適用于:相同數(shù)據(jù)集合,不同操作模式;此時,在GUI中定義客戶端數(shù)據(jù)處理驗證邏輯,不同的狀態(tài)對象封裝了不同的操作模式;
          • 方式2適用于:不同數(shù)據(jù)集合,相同操作模式;此時,在狀態(tài)對象中定義客戶端數(shù)據(jù)處理驗證邏輯,不同的狀態(tài)對象封裝了不同的數(shù)據(jù)集合操作。

          2.1 類型1: Read-Only & Normal
          2.1.1 動機

          客戶端GUI接受用戶輸入,經(jīng)過數(shù)據(jù)有效性驗證,然后將數(shù)據(jù)傳輸?shù)椒?wù)端,服務(wù)端檢查業(yè)務(wù)邏輯有效性,保存數(shù)據(jù);但是在特定情況下(比如數(shù)據(jù)已經(jīng)存在、且只能經(jīng)歷一次輸入),依據(jù)客戶端GUI所操作數(shù)據(jù)的狀態(tài),業(yè)務(wù)邏輯要求數(shù)據(jù)為只讀,即客戶端只具有顯示數(shù)據(jù)的功能,并不能改變服務(wù)器上的數(shù)據(jù)。

          一般地,編程實現(xiàn)時,會在GUI程序中加入判斷數(shù)據(jù)是否應(yīng)該為只讀的邏輯判斷語句。如果針對相同的數(shù)據(jù)集合(Model),有多個客戶端GUI(View),就需要在每個程序中加入該判斷。如此降低了程序的可維護性,決定數(shù)據(jù)是否為只讀的這樣一個業(yè)務(wù)邏輯將分散在程序中多處,不易維護,在業(yè)務(wù)邏輯發(fā)生改變時,易造成不一致。

          我們可以將變化部分(在Normal和Read-Only狀態(tài)下不同的部分)從GUI中抽取出來,分別用不同的類來表示,這樣,當(dāng)GUI的狀態(tài)發(fā)生改變時,只需要改變其對狀態(tài)對象的引用即可,狀態(tài)相關(guān)操作委托給狀態(tài)對象去完成。

          2.1.2 適用性

          本類型適用環(huán)境:相同數(shù)據(jù)集合,不同操作模式。即特定的GUI根據(jù)操作上下文環(huán)境,改變其響應(yīng)模式,根據(jù)數(shù)據(jù)是否可編輯,分為兩種狀態(tài)Read-Only State、 Normal State。

          2.1.3 結(jié)構(gòu)(圖2)

          圖2 相同數(shù)據(jù)集合,不同操作模式
          圖2  相同數(shù)據(jù)集合,不同操作模式

          2.1.4 參與者

          • ClientStateChangeable:客戶端GUI提供給State對象的操作接口,使得State對象可以訪問GUI的成員,以完成該狀態(tài)相關(guān)操作;
            • getChangeableComponents():返回在狀態(tài)轉(zhuǎn)換為Read-Only時,不可編輯的控件Collection;
            • saveChangeToServer():在可編輯狀態(tài)時,將客戶端數(shù)據(jù)保存到服務(wù)端。
          • AClientGUI:完成圖1中的Context功能,即其狀態(tài)要進行改變的客戶端GUI;維護ClientState的一個實例(state);
            • init():將this引用作為參數(shù),調(diào)用state.setComponents(this) 以設(shè)置可變控件的初始狀態(tài);
            • okButtonActionPerformed(e:ActionEvent):用戶完成操作后,本方法可作為 "OK"按鈕控件的ActionListener方法,調(diào)用state.action(this) 以完成本操作。
          • ClientState:State接口,封裝與客戶端GUI某個特定狀態(tài)相關(guān)的行為;
            • setComponents(gui:ClientStateChangeable):設(shè)置參數(shù)gui指定的客戶端GUI的控件狀態(tài);
            • action(gui:ClientStateChangeable):完成數(shù)據(jù)保存功能。
          • ClientNormalState:可編輯狀態(tài)子類,實現(xiàn)ClientState接口;
            • setComponents(gui:ClientStateChangeable):調(diào)用參數(shù)gui指定的客戶端GUI的getChangeableComponents()獲取控件Collection,然后遍歷設(shè)置各控件狀態(tài)為可編輯;
            • action(gui:ClientStateChangeable):調(diào)用參數(shù)gui指定的客戶端GUI的saveChangeToServer()完成數(shù)據(jù)保存功能。
          • ClientReadOnlyState:只讀狀態(tài)子類,實現(xiàn)ClientState接口;
            • setComponents(gui:ClientStateChangeable):調(diào)用參數(shù)gui指定的客戶端GUI的getChangeableComponents()獲取控件Collection,然后遍歷設(shè)置各控件狀態(tài)為ReadOnly;
            • action(gui:ClientStateChangeable):空方法,本狀態(tài)下無數(shù)據(jù)變化,無須保存。

          2.1.5 代碼示例

          ClientStateChangeable接口:


          
          public interface ClientStateChangeable {	
          	/**
          	 * To get all changeable components in the GUI object. 
          	 * @return Collection contains Component objects. 
          	 */
          	Collection getChageableComponents();
          	
          	/**
          	 * To save data to server.
          	 */
          	void saveChangeToServer();
          }
          

          AClientGUI類:


          
          public class AClientGUI extends JPanel implements ClientStateChangeable{
          	//… 	
          	private ClientState state = null;
          
          public DesignStep1View(ClientState state){
          //…			
          this.state = state;
          this.state.setComponents(this);
          	}
          	
          	private void okButton_actionPerformed(ActionEvent e){
          		state.action(this);	//save data
          	}
          
          	public Collection getChageableComponents() {
          		Collection dataComponents = new ArrayList();		
          		dataComponents.add(jComboBoxESEPattern);
          		//…			
          		return dataComponents;
          	}
          
          	public void saveChangeToServer() {
          		//…	
          	}
          }
          

          ClientState接口:

          public interface ClientState { /** * To set components' state in the GUI. * @param gui a GUI object which implements StateChangeable interface. */ void setComponents(ClientStateChangeable gui); /** * when user click OK-Button in a GUI, the GUI will call this method. * @param gui a GUI object which implements StateChangeable interface. */ void action(ClientStateChangeable gui); }

          ClientNormalState類:


          olor="#CCCCCC">
          
          public class ClientNormalState implements ClientState {
          	/**
          	 * 正常狀態(tài)下, 各個控件默認為可編輯的, 所以不用做任何更改
          	 */
          	public void setComponents(ClientStateChangeable gui) {}
          
          	/** 
          	 * 正常狀態(tài)下, 需要將用戶所作修改保存到服務(wù)端 
          	 */
          	public void action(ClientStateChangeable gui) {
          		gui.saveChangeToServer();
          	}
          }
          

          ClientReadOnlyState類:


          
          public class ClientReadOnlyState implements ClientState {
          	/**
          	 * 設(shè)置GUI的數(shù)據(jù)控件為Read-Only
          	 */
          public void setComponents(ClientStateChangeable gui) {
          		Collection components = gui.getChageableComponents();
          		Iterator iter = components.iterator();
          		while(iter.hasNext()){
          			JComponent jc = (JComponent)iter.next();
          			jc.setEnabled(false);			
          			String toolTip = jc.getToolTipText();
          			String addedTip = "只讀狀態(tài)";
          			if(toolTip == null)toolTip = addedTip;
          			else toolTip += ". " + addedTip;
          			jc.setToolTipText(toolTip); 
          		}
          	}
          
          	/**
          	 * GUI處于Read-Only狀態(tài), 無需將數(shù)據(jù)保存到server端	
          	 */
          	public void action(ClientStateChangeable gui) {}
          }
          

          2.2 類型2:(Reuse GUI)
          2.2.1 動機

          當(dāng)多個客戶端GUI布局、控件類型很相似,所完成的任務(wù)也相似時,只需要經(jīng)過精心設(shè)計,將這些GUI的展示形式統(tǒng)一起來,同一個GUI可以用到多個場景中,達到重用的目的。此時,這些不同任務(wù)需要操作不同的數(shù)據(jù)集合。

          可以在GUI類中實現(xiàn)這些不同數(shù)據(jù)集合的操作,但是這會給程序維護帶來麻煩。首先,屬于不同邏輯的數(shù)據(jù)操作出現(xiàn)在同一類文件中,造成邏輯混亂、程序規(guī)模增大,不易于調(diào)試;其次,要將GUI用于新的數(shù)據(jù)集合時,只能在相同文件中增加新的代碼,此時,該程序的可維護性降低,尤其是新的工作由其他程序員完成時,要理解原有代碼是很費力的。

          和2.1.1節(jié)中提到的解決方法類似:將變化的部分和不變部分分離開來,使得變化的部分可以獨立修改、擴充。具體地,則是將數(shù)據(jù)集合相關(guān)操作從GUI程序中抽取出來,定義一個所有數(shù)據(jù)集合操作的接口(即:狀態(tài)接口),不同地數(shù)據(jù)集合操作作為該接口的一個實現(xiàn)類存在。這樣,每個數(shù)據(jù)集合都獨立的封裝于一個狀態(tài)對象內(nèi);而且,要對新的數(shù)據(jù)使用該GUI,只需要定義新的狀態(tài)接口實現(xiàn)類即可,無須修改已有類,甚至不關(guān)心已有的狀態(tài)。

          2.2.2 適用性

          本類型適用環(huán)境:不同的數(shù)據(jù)集合,相同的操作模式。即不變化的客戶端GUI,將不同的數(shù)據(jù)集合操作委托給變化的狀態(tài)對象去完成。

          2.2.3 結(jié)構(gòu)(圖3)

          圖3 不同數(shù)據(jù)集合,相同操作模式
          圖3 不同數(shù)據(jù)集合,相同操作模式

          2.2.4 參與者

          • InvariableGUI:本身不發(fā)生變化的客戶端GUI類,維護一個對VariableDataState的引用state,將數(shù)據(jù)相關(guān)操作委托給該引用對象 ;
            • saveChangeToServer():調(diào)用state.processData(this)完成數(shù)據(jù)相關(guān)操作;
          • VariableDataState:State接口,封裝與數(shù)據(jù)相關(guān)操作的行為;
            • ProcessData(gui:InvariableGUI):調(diào)用參數(shù)gui的成員獲取數(shù)據(jù)并完成處理;
          • DataState1、DataState2、 … … DataStateN:狀態(tài)子類,實現(xiàn)VariableDataState接口,封裝了特定某一類數(shù)據(jù)集合的操作,可以根據(jù)不同的數(shù)據(jù)集合定義多個VariableDataState接口的狀態(tài)類,從而實現(xiàn)了對InvariableGUI的重用。

          本類型的實現(xiàn)代碼在這里就不列出了,參照2.1.5節(jié)中的代碼,很容易的就可實現(xiàn)本類型的結(jié)構(gòu)。

          2.3 綜合以上兩種類型
          可以將以上兩種類型結(jié)合起來使用,即實現(xiàn)了客戶端軟件的數(shù)據(jù)集合方面對GUI的重用,也實現(xiàn)了操作模式方面對GUI的重用。

          程序?qū)崿F(xiàn)時,可以由GUI類分別維護一個ClientState的引用和一個VariableDataState的引用:

          • 初始化GUI類時,GUI的構(gòu)造器調(diào)用ClientState對象的setComponents()方法設(shè)置控件的狀態(tài);
          • 用戶提交操作時,GUI調(diào)用ClientState對象的action()方法,如圖2所示,該方法使用傳遞的gui參數(shù)回調(diào)GUI的saveChangeToServer()方法,而saveChangeToServer()方法則按照圖3所示,調(diào)用VariableDataState引用的狀態(tài)對象的processData()方法完成數(shù)據(jù)操作。

          3 總結(jié)
          本文介紹的State模式應(yīng)用于多類型數(shù)據(jù)、多操作模式的客戶端軟件,可以取得明顯的效果;但如果客戶端類和狀態(tài)都很少時,使用本模式,反而增加了客戶端類數(shù)量,增加了體系結(jié)構(gòu)的復(fù)雜性,此時,可以使用繼承方式的類體系來實現(xiàn)重用,無須使用State狀態(tài)對象的委托操作和回調(diào)操作。

          posted on 2005-02-05 20:09 jacky 閱讀(398) 評論(0)  編輯  收藏 所屬分類: Design Patten

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          <2025年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿(10)

          隨筆檔案

          文章分類

          文章檔案

          相冊

          收藏夾

          java

          搜索

          •  

          最新評論


          主站蜘蛛池模板: 会理县| 东阳市| 当阳市| 菏泽市| 成安县| 大余县| 浮梁县| 长葛市| 慈利县| 乐山市| 公安县| 富锦市| 哈巴河县| 旬邑县| 丽水市| 运城市| 天柱县| 台州市| 沙坪坝区| 盱眙县| 宜州市| 时尚| 电白县| 苏州市| 北流市| 青铜峡市| 福安市| 亚东县| 河北区| 永年县| 通河县| 永嘉县| 雷州市| 长垣县| 渝北区| 平原县| 德清县| 政和县| 介休市| 托里县| 南川市|