熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

在Java中應用State設計模式

2013-11-23 19:50:31  來源: Java高級技術 

  對象的狀態由各個屬性的當前值構成當我們調用某個對象的setXXX()方法時通常表示修改它的XXX屬性另外對象在執行方法時也可能修改自己的狀態在某些情形下例如建立事務或機器模型時對象的狀態可能是決定其行為的關鍵因素依賴於狀態的代碼邏輯可能遍布於類的大量方法State模式的目標就是簡化這類代碼把依賴於狀態的邏輯集中到一組類每一個類代表一種不同的狀態避免if語句嵌套過深或過於復雜轉而依賴於多態性來調用不同的方法
  狀態模型
  如果對象的狀態信息很關鍵對象會擁有一些變量來指示如何根據狀態做出相應的動作這些變量大量地散布於復雜的多層嵌套if語句中來描述對象如何響應可能出現的事件用這種方式建立對象模型的最大缺點在於if語句可能變得相當復雜一旦要修改對象的狀態模型往往有多個方法的許多if語句需要調整
  
  以傳送帶的門為例考慮其狀態變化過程為傳送帶的門由單個按鈕控制並且假設初始時處於關閉狀態按一下按鈕門開始打開如果在門完全打開之前再次按下按鈕門開始關閉一旦門完全打開它將在秒延時之後自動開始關閉過程要禁止門自動關閉可以在門打開之後按一下按鈕描述了傳送門的狀態變化情況它是一個UML狀態機(State Machine)其中click表示按下按鈕的動作顯然與純文字描述相比UML狀態機圖示更加直觀易懂
  
  按照常規的設計思路(不使用State設計模式)在模擬傳送帶工作過程的軟件中可以使用一個Door對象代表傳送門(如圖所示)狀態改變事件由傳送帶軟件發送給Door對象
   
  圖 UML狀態機
   
  圖 狀態改變事件發送給Door對象
  Door類從Observable派生這樣客戶程序(例如一個GUI程序)就能夠方便地了解傳送門狀態Door類首先定義傳送門可能處於的狀態代碼如下
  
  public class Door extends Observable {
  public static final int CLOSED  = ;
  public static final int OPENING = ;
  public static final int OPEN   = ;
  public static final int CLOSING = ;
  public static final int STAYOPEN = ;
  private int state = CLOSED;
  //
  }
  status()方法返回傳送門狀態的文字描述如下所示
  
  public String status() {
  switch (state) {
    case OPENING :
      return "正在打開";
    case OPEN :
      //
    default :
      return "已關閉";
  }
  }
  當用戶點擊傳送帶的按鈕時傳送帶程序調用Door對象的click()方法click()方法模擬圖所示的狀態裝換過程
  
  public void click() {
  if (state == CLOSED) {
    setState(OPENING);
  }
  else if (state == OPENING || state == STAYOPEN) {
    setState(CLOSING);
  }
  else if (state == OPEN) {
    setState(STAYOPEN);
  }
  else if (state == CLOSING) {
    setState(OPENING);
  }
  }
  Door類的setState()方法向觀察者通知傳送門狀態改變事件代碼如下
  
  private void setState(int state) {
  thisstate = state;
  setChanged();
  notifyObservers();
  }
  用State模式改造
  Door類的代碼比較復雜整個類到處都用到了狀態變量如果要比較圖的狀態機和Door類的各個狀態變換方法將是非常困難的click()方法尤其如此那麼怎樣在這個例子中應用State模式呢?首先要把傳送門的各種狀態分別定義成類如圖所示能夠更好地與圖的狀態機對應更改後的類設計中Door包含了狀態機的上下文信息所謂上下文信息就是描述環境和一系列其它對象相關的信息就本例而言State利用一個上下文對象記錄了傳送門的當前狀態是DoorState類的哪一個實例
  
  圖 傳送門各個狀態
  DoorState類的構造函數要求提供一個Door對象DoorState的子類利用該對象傳達狀態變更信息在這種設計方案中DoorState的子類通過一個Door類型的屬性綁定到特定的傳送門(Door)對象因而要求一個DoorState對象只被一個Door對象引用同時Door類要把它的狀態信息定義成局部變量代碼如下
  
  public class Door extends Observable {
  public final DoorState CLOSED  = new DoorClosed(this);
  // 按照類似方式定義DoorState類型的
  // OPENINGOPENCLOSINGSTAYOPEN對象(略)
  private DoorState state = CLOSED;
  //
  }
  DoorState類是一個抽象類由子類實現其click()方法在狀態機中每一個狀態均有相應的按下按鈕操作修改後的設計中每一個描述狀態的類也有一個click()方法兩者是一致的DoorState類處理了其它可能的變換所以DoorState的子類可以忽略無關的事件代碼如下
  
  public abstract class DoorState {
  protected Door door;
  public DoorState(Door door) {
    thisdoor = door;
  }
  public abstract void click();
  public String status() {
    String s = getClass()getName();
    return ssubstring(slastIndexOf() + );
  }
   public void complete() { }
   public void timeout() { }
  }
  由上可以看到現在的status()方法要比修改設計方案之前的status()方法簡單多了新status()方法返回的結果與修改前版本的結果略有不同它的狀態信息從類的名稱獲得如果要返回修改設計方案之前的信息只需把這些狀態信息分別記錄到DoorState的各個子類中然後在這個status()方法中直接提取即可
  
  新的設計方案中傳送門對象(Door)從傳送帶接收狀態改變信息的這一角色仍未改變但現在Door對象只需把這些狀態改變信息直接傳遞給當前的狀態對象就可以了代碼如下
  
  public class Door extends Observable {
  // 聲明變量
  protected void setState(DoorState state) {
    thisstate = state;
    setChanged();
    notifyObservers();
  }
  public void click() {
    stateclick();
  }
  // complete()status()timeout()都直接
  // 調用state的相應方法即可(略)
  }
  這裡的click()complete()status()和timeout()方法體現了Java類多態性的應用所有這些方法都起著判斷和選擇動作的作用即是雖然每一個方法的代碼是不含if判斷邏輯的但實際運行時被調用的狀態對象卻不斷變化在調用click()時會發生哪些事情呢?按照多態性規則答案依賴於當時傳送門的狀態修改後的代碼有效地擔負起了根據狀態執行不同動作的任務但由於利用了多態性它變得更加簡單了
  
  Door類中的setState()方法現在由DoorState的子類調用這些DoorState的子類與圖狀態機中的相應實體很相似例如狀態機中Open狀態包含Timeout和ClickDoorOpen類則包含兩個對應的方法timeout()和click()DoorOpen類的代碼如下
  
  public class DoorOpen extends DoorState {
  public DoorOpen(Door door) {
    super(door);
  }
  public void click() {
    doorsetState(doorSTAYOPEN);
  }
  public void timeout() {
    doorsetState(doorCLOSING);
  }
  }
  從上面可以看到利用State設計模式之後代碼變得更簡單了不過細心的讀者或許已經注意到Door類用到的常量實際上是變量這給人一種不規范的感覺假設現在要把這些狀態常量移到_DoorConstant接口這就需要從DoorState類消除Door實例變量修改辦法是重新定義DoorState類中的click()complete()和timeout()變換方法把一個Door對象以參數的形式傳遞給它們按照這種設計方法Door對象調用狀態變換方法例如click()時將采用stateclick(this)的形式
  
  
  
  
  
  
  
  

From:http://tw.wingwit.com/Article/program/Java/gj/201311/27535.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.