State模式在用戶端軟體中的應用

來源:互聯網
上載者:User

http://35java.com/zhibo/forum.php?mod=viewthread&tid=109&extra=page%3D3

引言
      在分層軟體體繫結構中,服務端程式關注於實現商務邏輯,用戶端程式則包含使用者介面。服務端程式由用戶端程式調用,其請求、響應模式在設計時已經確定,運行時出現問題的機率較小。相反,用戶端程式與使用者直接互動,雖然有正確規定的操作順序或模式,但是使用者的操作是不可預知的,程式必須處理各種操作錯誤、加上資料輸入有效驗證等要求,使得用戶端程式的開發成本上升。
      因而,一旦有經過充分測試的、甚至是通過驗收的使用者互動程式GUI,應該儘可能的重用該GUI,以提高軟體的可靠性、可維護性。
      在對一個J2EE項目的重構、增加新功能的過程中,對用戶端GUI程式,我們使用了State模式。結果顯示,該模式的使用,不但減少了用戶端GUI程式的程式規模(LOC),而且,該部分的開發及單元測試時間大大減少,同時,在整合測試中發現的缺陷數量比使用該模式前平均減少了3倍。本文就該項目中使用State模式的方式進行介紹。
      

1. State模式
      首先,先簡單介紹一下State模式。
      該模式是指在對象的內部狀態改變時改變對象的行為【1】。其結構1所示。
               
圖1 State模式結構
              
      模式中各個參與者職責簡介如下:
      

  • Context:使用者物件,擁有一個State類型的成員,以標識對象的目前狀態;
  • State:介面或基類,封裝與Context的特定狀態相關的行為;
  • ConcreteState:介面實作類別或子類,實現了一個與Context某個狀態相關的行為。

                                運行時,Context將與狀態相關的請求委託給當前的ConcreteState對象處理。關於State模式更詳盡的介紹,請參閱參考文獻1。
      

2. 用戶端應用
      本模式的目標是分離用戶端軟體中的變化部分與不變部分,以使得變化的部分可獨立於不變的部分,有利於擴充新的功能,也有利於維護。
      在項目中,對於用戶端GUI的重用有兩種方式。
      

  • 方式1適用於:相同資料集合,不同操作模式;此時,在GUI中定義用戶端資料處理驗證邏輯,不同的狀態物件封裝了不同的操作模式;
  • 方式2適用於:不同資料集合,相同操作模式;此時,在狀態物件中定義用戶端資料處理驗證邏輯,不同的狀態物件封裝了不同的資料集合操作。

      2.1 類型1: Read-Only & Normal
              2.1.1 動機      
      用戶端GUI接受使用者輸入,經過資料有效性驗證,然後將資料轉送到服務端,服務端檢查商務邏輯有效性,儲存資料;但是在特定情況下(比如資料已經存在、且只能經曆一次輸入),依據用戶端GUI所操作資料的狀態,商務邏輯要求資料為唯讀,即用戶端只具有顯示資料的功能,並不能改變伺服器上的資料。
       一般地,編程實現時,會在GUI程式中加入判斷資料是否應該為唯讀邏輯判斷語句。如果針對相同的資料集合(Model),有多個用戶端GUI(View),就需要在每個程式中加入該判斷。如此降低了程式的可維護性,決定資料是否為唯讀這樣一個商務邏輯將分散在程式中多處,不易維護,在商務邏輯發生改變時,易造成不一致。
      我們可以將變化部分(在Normal和Read-Only狀態下不同的部分)從GUI中抽取出來,分別用不同的類來表示,這樣,當GUI的狀態發生改變時,只需要改變其對狀態物件的引用即可,狀態相關操作委託給狀態物件去完成。
              2.1.2 適用性      
      本類型適用環境:相同資料集合,不同操作模式。即特定的GUI根據操作上下文環境,改變其響應模式,根據資料是否可編輯,分為兩種狀態Read-Only State、 Normal State。
              2.1.3 結構(圖2)      
              
圖2  相同資料集合,不同操作模式
              
               2.1.4 參與者      
      

  • ClientStateChangeable:用戶端GUI提供給State對象的操作介面,使得State對象可以訪問GUI的成員,以完成該狀態相關操作;
    • getChangeableComponents():返回在狀態轉換為Read-Only時,不可編輯的控制項Collection;
    • saveChangeToServer():在可編輯狀態時,將用戶端資料儲存到服務端。

                             

  • AClientGUI:完成圖1中的Context功能,即其狀態要進行改變的用戶端GUI;維護ClientState的一個執行個體(state);
    • init():將this引用作為參數,調用state.setComponents(this) 以設定可變控制項的初始狀態;
    • okButtonActionPerformed(e:ActionEvent):使用者完成操作後,本方法可作為 "OK"按鈕控制項的ActionListener方法,調用state.action(this) 以完成本操作。

                             

  • ClientState:State介面,封裝與用戶端GUI某個特定狀態相關的行為;
    • setComponents(gui:ClientStateChangeable):設定參數gui指定的用戶端GUI的控制項狀態;
    • action(gui:ClientStateChangeable):完成資料儲存功能。

                             

  • ClientNormalState:可編輯狀態子類,實現ClientState介面;
    • setComponents(gui:ClientStateChangeable):調用參數gui指定的用戶端GUI的getChangeableComponents()擷取控制項Collection,然後遍曆設定各控制項狀態為可編輯;
    • action(gui:ClientStateChangeable):調用參數gui指定的用戶端GUI的saveChangeToServer()完成資料儲存功能。

                             

  • ClientReadOnlyState:唯讀狀態子類,實現ClientState介面;
    • setComponents(gui:ClientStateChangeable):調用參數gui指定的用戶端GUI的getChangeableComponents()擷取控制項Collection,然後遍曆設定各控制項狀態為ReadOnly;
    • action(gui:ClientStateChangeable):空方法,本狀態下無資料變化,無須儲存。

                             

                                                          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類:
            

public class ClientNormalState implements ClientState {
        /**
         * 正常狀態下, 各個控制項預設為可編輯的, 所以不用做任何更改
         */
        public void setComponents(ClientStateChangeable gui) {}
        /**
         * 正常狀態下, 需要將使用者所作修改儲存到服務端
         */
        public void action(ClientStateChangeable gui) {
                gui.saveChangeToServer();
        }
}

      ClientReadOnlyState類:
            

public class ClientReadOnlyState implements ClientState {
        /**
         * 設定GUI的資料控制項為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 = "唯讀狀態";
                        if(toolTip == null)toolTip = addedTip;
                        else toolTip += ". " + addedTip;
                        jc.setToolTipText(toolTip);
                }
        }
        /**
         * GUI處於Read-Only狀態, 無需將資料儲存到server端       
         */
        public void action(ClientStateChangeable gui) {}
}

              2.2 類型2:(Reuse GUI)
              2.2.1 動機      
      當多個用戶端GUI布局、控制項類型很相似,所完成的任務也相似時,只需要經過精心設計,將這些GUI的展示形式統一起來,同一個GUI可以用到多個情境中,達到重用的目的。此時,這些不同任務需要操作不同的資料集合。
      可以在GUI類中實現這些不同資料集合的操作,但是這會給程式維護帶來麻煩。首先,屬於不同邏輯的資料操作出現在同一類檔案中,造成邏輯混亂、程式規模增大,不易於調試;其次,要將GUI用於新的資料集合時,只能在相同檔案中增加新的代碼,此時,該程式的可維護性降低,尤其是新的工作由其他程式員完成時,要理解原有代碼是很費力的。
      和2.1.1節中提到的解決方案類似:將變化的部分和不變部分分離開來,使得變化的部分可以獨立修改、擴充。具體地,則是將資料集合相關操作從GUI程式中抽取出來,定義一個所有資料集合操作的介面(即:狀態介面),不同地資料集合操作作為該介面的一個實作類別存在。這樣,每個資料集合都獨立的封裝於一個狀態物件內;而且,要對新的資料使用該GUI,只需要定義新的狀態介面實作類別即可,無須修改已有類,甚至不關心已有的狀態。
              2.2.2 適用性      
      本類型適用環境:不同的資料集合,相同的操作模式。即不變化的用戶端GUI,將不同的資料集合操作委託給變化的狀態物件去完成。
              2.2.3 結構(圖3)      
               
圖3 不同資料集合,相同操作模式
              
              2.2.4 參與者      
      

  • InvariableGUI:本身不發生變化的用戶端GUI類,維護一個對VariableDataState的引用state,將資料相關操作委託給該引用對象 ;
    • saveChangeToServer():調用state.processData(this)完成資料相關操作;

                    

  • VariableDataState:State介面,封裝與資料相關操作的行為;
    • ProcessData(gui:InvariableGUI):調用參數gui的成員擷取資料並完成處理;

                    

  • DataState1、DataState2、 … … DataStateN:狀態子類,實現VariableDataState介面,封裝了特定某一類資料集合的操作,可以根據不同的資料集合定義多個VariableDataState介面的狀態類,從而實現了對InvariableGUI的重用。

                       本類型的實現代碼在這裡就不列出了,參照2.1.5節中的代碼,很容易的就可實現本類型的結構。
      2.3 綜合以上兩種類型
      可以將以上兩種類型結合起來使用,即實現了用戶端軟體的資料集合方面對GUI的重用,也實現了操作模式方面對GUI的重用。
      程式實現時,可以由GUI類分別維護一個ClientState的引用和一個VariableDataState的引用:
      

  • 初始化GUI類時,GUI的構造器調用ClientState對象的setComponents()方法設定控制項的狀態;
  • 使用者提交操作時,GUI調用ClientState對象的action()方法,2所示,該方法使用傳遞的gui參數回調GUI的saveChangeToServer()方法,而saveChangeToServer()方法則按照圖3所示,調用VariableDataState引用的狀態物件的processData()方法完成資料操作。

                      

3 總結
      本文介紹的State模式應用於多類型資料、多操作模式的用戶端軟體,可以取得明顯的效果;但如果用戶端類和狀態都很少時,使用本模式,反而增加了用戶端類數量,增加了體繫結構的複雜性,此時,可以使用繼承方式的類體系來實現重用,無須使用State狀態物件的委託操作和回調操作。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.