《JAVA與模式》第23天—狀態模式 – JAVA編程語言程序開發技術文章

狀態模式,又稱狀態對象模式(Pattern of Objects for States),狀態模式是對象的行為模式。

  狀態模式允許一個對象在其內部狀態改變的時候改變其行為。這個對象看上去就像是改變瞭它的類一樣。

狀態模式的結構
  用一句話來表述,狀態模式把所研究的對象的行為包裝在不同的狀態對象裡,每一個狀態對象都屬於一個抽象狀態類的一個子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行為也隨之改變。狀態模式的示意性類圖如下所示:

 

  狀態模式所涉及到的角色有:

  ●  環境(Context)角色,也成上下文:定義客戶端所感興趣的接口,並且保留一個具體狀態類的實例。這個具體狀態類的實例給出此環境對象的現有狀態。

  ●  抽象狀態(State)角色:定義一個接口,用以封裝環境(Context)對象的一個特定的狀態所對應的行為。

  ●  具體狀態(ConcreteState)角色:每一個具體狀態類都實現瞭環境(Context)的一個狀態所對應的行為。

  源代碼
  環境角色類

[java] 
package com.bankht.State; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:34:57
 * 
 * @類說明 :環境角色類
 */ 
public class Context { 
    // 持有一個State類型的對象實例  
    private State state; 
 
    public void setState(State state) { 
        this.state = state; 
    } 
 
    /**
     * 用戶感興趣的接口方法
     */ 
    public void request(String sampleParameter) { 
        // 轉調state來處理  
        state.handle(sampleParameter); 
    } 

package com.bankht.State;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:34:57
 *
 * @類說明 :環境角色類
 */
public class Context {
 // 持有一個State類型的對象實例
 private State state;

 public void setState(State state) {
  this.state = state;
 }

 /**
  * 用戶感興趣的接口方法
  */
 public void request(String sampleParameter) {
  // 轉調state來處理
  state.handle(sampleParameter);
 }
}
 

 

  抽象狀態類

[java]
package com.bankht.State; 
/** 
 * @author: 特種兵—AK47 
 * @創建時間:2012-7-3 上午09:35:29 
 *
 * @類說明 :抽象狀態類
 
 */ 
public interface State { 
    /**
     * 狀態對應的處理
     */ 
    public void handle(String sampleParameter); 

package com.bankht.State;
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:35:29
 *
 * @類說明 :抽象狀態類

 */
public interface State {
    /**
     * 狀態對應的處理
     */
    public void handle(String sampleParameter);
}
 

 

  具體狀態類

[java] 
package com.bankht.State; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:35:52
 * 
 * @類說明 :具體狀態類
 */ 
public class ConcreteStateA implements State { 
 
    @Override 
    public void handle(String sampleParameter) { 
 
        System.out.println("ConcreteStateA handle :" + sampleParameter); 
    } 
 

package com.bankht.State;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:35:52
 *
 * @類說明 :具體狀態類
 */
public class ConcreteStateA implements State {

 @Override
 public void handle(String sampleParameter) {

  System.out.println("ConcreteStateA handle :" + sampleParameter);
 }

}
 

 

[java] 
package com.bankht.State; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:36:15
 * 
 * @類說明 :
 */ 
public class ConcreteStateB implements State { 
 
    @Override 
    public void handle(String sampleParameter) { 
 
        System.out.println("ConcreteStateB handle :" + sampleParameter); 
    } 
 

package com.bankht.State;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:36:15
 *
 * @類說明 :
 */
public class ConcreteStateB implements State {

 @Override
 public void handle(String sampleParameter) {

  System.out.println("ConcreteStateB handle :" + sampleParameter);
 }

}
 

 

  客戶端類

[java] 
package com.bankht.State; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:36:33
 * 
 * @類說明 :
 */ 
public class Client { 
 
    public static void main(String[] args) { 
        // 創建狀態  
        State state = new ConcreteStateB(); 
        // 創建環境  
        Context context = new Context(); 
        // 將狀態設置到環境中  
        context.setState(state); 
        // 請求  
        context.request("test"); 
    } 

package com.bankht.State;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:36:33
 *
 * @類說明 :
 */
public class Client {

 public static void main(String[] args) {
  // 創建狀態
  State state = new ConcreteStateB();
  // 創建環境
  Context context = new Context();
  // 將狀態設置到環境中
  context.setState(state);
  // 請求
  context.request("test");
 }
}
 

 

  從上面可以看出,環境類Context的行為request()是委派給某一個具體狀態類的。通過使用多態性原則,可以動態改變環境類Context的屬性State的內容,使其從指向一個具體狀態類變換到指向另一個具體狀態類,從而使環境類的行為request()由不同的具體狀態類來執行。

使用場景
  考慮一個在線投票系統的應用,要實現控制同一個用戶隻能投一票,如果一個用戶反復投票,而且投票次數超過5次,則判定為惡意刷票,要取消該用戶投票的資格,當然同時也要取消他所投的票;如果一個用戶的投票次數超過8次,將進入黑名單,禁止再登錄和使用系統。

  要使用狀態模式實現,首先需要把投票過程的各種狀態定義出來,根據以上描述大致分為四種狀態:正常投票、反復投票、惡意刷票、進入黑名單。然後創建一個投票管理對象(相當於Context)。

  系統的結構圖如下所示:

 

  源代碼
  抽象狀態類

[java]
package com.bankht.State.Vote; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:39:41
 * 
 * @類說明 :
 */ 
public interface VoteState { 
    /**
     * 處理狀態對應的行為
     * 
     * @param user
     *            投票人
     * @param voteItem
     *            投票項
     * @param voteManager
     *            投票上下文,用來在實現狀態對應的功能處理的時候, 可以回調上下文的數據
     */ 
    public void vote(String user, String voteItem, VoteManager voteManager); 

package com.bankht.State.Vote;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:39:41
 *
 * @類說明 :
 */
public interface VoteState {
 /**
  * 處理狀態對應的行為
  *
  * @param user
  *            投票人
  * @param voteItem
  *            投票項
  * @param voteManager
  *            投票上下文,用來在實現狀態對應的功能處理的時候, 可以回調上下文的數據
  */
 public void vote(String user, String voteItem, VoteManager voteManager);
}
 

 

  具體狀態類——正常投票

[java] 
package com.bankht.State.Vote; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:40:00
 * 
 * @類說明 :具體狀態類——正常投票
 */ 
public class NormalVoteState implements VoteState { 
 
    @Override 
    public void vote(String user, String voteItem, VoteManager voteManager) { 
        // 正常投票,記錄到投票記錄中  
        voteManager.getMapVote().put(user, voteItem); 
        System.out.println("恭喜投票成功"); 
    } 
 

package com.bankht.State.Vote;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:40:00
 *
 * @類說明 :具體狀態類——正常投票
 */
public class NormalVoteState implements VoteState {

 @Override
 public void vote(String user, String voteItem, VoteManager voteManager) {
  // 正常投票,記錄到投票記錄中
  voteManager.getMapVote().put(user, voteItem);
  System.out.println("恭喜投票成功");
 }

}

 

  具體狀態類——重復投票

[java] 
package com.bankht.State.Vote; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:40:23
 * 
 * @類說明 :具體狀態類——重復投票
 */ 
public class RepeatVoteState implements VoteState { 
 
    @Override 
    public void vote(String user, String voteItem, VoteManager voteManager) { 
        // 重復投票,暫時不做處理  
        System.out.println("請不要重復投票"); 
    } 
 

package com.bankht.State.Vote;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:40:23
 *
 * @類說明 :具體狀態類——重復投票
 */
public class RepeatVoteState implements VoteState {

 @Override
 public void vote(String user, String voteItem, VoteManager voteManager) {
  // 重復投票,暫時不做處理
  System.out.println("請不要重復投票");
 }

}

 

  具體狀態類——惡意刷票

[java] 
package com.bankht.State.Vote; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:40:50
 * 
 * @類說明 :具體狀態類——惡意刷票
 */ 
public class SpiteVoteState implements VoteState { 
 
    @Override 
    public void vote(String user, String voteItem, VoteManager voteManager) { 
        // 惡意投票,取消用戶的投票資格,並取消投票記錄  
        String str = voteManager.getMapVote().get(user); 
        if (str != null) { 
            voteManager.getMapVote().remove(user); 
        } 
        System.out.println("你有惡意刷屏行為,取消投票資格"); 
    } 
 

package com.bankht.State.Vote;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:40:50
 *
 * @類說明 :具體狀態類——惡意刷票
 */
public class SpiteVoteState implements VoteState {

 @Override
 public void vote(String user, String voteItem, VoteManager voteManager) {
  // 惡意投票,取消用戶的投票資格,並取消投票記錄
  String str = voteManager.getMapVote().get(user);
  if (str != null) {
   voteManager.getMapVote().remove(user);
  }
  System.out.println("你有惡意刷屏行為,取消投票資格");
 }

}
 

 

  具體狀態類——黑名單

[java] 
package com.bankht.State.Vote; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:41:15
 * 
 * @類說明 :具體狀態類——黑名單
 */ 
public class BlackVoteState implements VoteState { 
 
    @Override 
    public void vote(String user, String voteItem, VoteManager voteManager) { 
        // 記錄黑名單中,禁止登錄系統  
        System.out.println("進入黑名單,將禁止登錄和使用本系統"); 
    } 
 

package com.bankht.State.Vote;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:41:15
 *
 * @類說明 :具體狀態類——黑名單
 */
public class BlackVoteState implements VoteState {

 @Override
 public void vote(String user, String voteItem, VoteManager voteManager) {
  // 記錄黑名單中,禁止登錄系統
  System.out.println("進入黑名單,將禁止登錄和使用本系統");
 }

}
 
  環境類

[java] 
package com.bankht.State.Vote; 
 
import java.util.HashMap; 
import java.util.Map; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:41:35
 * 
 * @類說明 :環境類
 */ 
public class VoteManager { 
    // 持有狀體處理對象  
    private VoteState state = null; 
    // 記錄用戶投票的結果,Map<String,String>對應Map<用戶名稱,投票的選項>  
    private Map<String, String> mapVote = new HashMap<String, String>(); 
    // 記錄用戶投票次數,Map<String,Integer>對應Map<用戶名稱,投票的次數>  
    private Map<String, Integer> mapVoteCount = new HashMap<String, Integer>(); 
 
    /**
     * 獲取用戶投票結果的Map
     */ 
    public Map<String, String> getMapVote() { 
        return mapVote; 
    } 
 
    /**
     * 投票
     * 
     * @param user
     *            投票人
     * @param voteItem
     *            投票的選項
     */ 
    public void vote(String user, String voteItem) { 
        // 1.為該用戶增加投票次數  
        // 從記錄中取出該用戶已有的投票次數  
        Integer oldVoteCount = mapVoteCount.get(user); 
        if (oldVoteCount == null) { 
            oldVoteCount = 0; 
        } 
        oldVoteCount += 1; 
        mapVoteCount.put(user, oldVoteCount); 
        // 2.判斷該用戶的投票類型,就相當於判斷對應的狀態  
        // 到底是正常投票、重復投票、惡意投票還是上黑名單的狀態  
        //下面使用本人自創常用的“假設修正法” 完成switch的結構  
        if(oldVoteCount == 1 ) { 
            state = new NormalVoteState(); 
        } 
        if(oldVoteCount > 1 ) { 
            state = new RepeatVoteState(); 
        } 
        if(oldVoteCount > 5 ) { 
            state = new SpiteVoteState(); 
        } 
        if(oldVoteCount > 8 ) { 
            state = new BlackVoteState(); 
        } 
         
//      if (oldVoteCount == 1) {  
//          state = new NormalVoteState();  
//      } else if (oldVoteCount > 1 && oldVoteCount < 5) {  
//          state = new RepeatVoteState();  
//      } else if (oldVoteCount >= 5 && oldVoteCount < 8) {  
//          state = new SpiteVoteState();  
//      } else if (oldVoteCount > 8) {  
//          state = new BlackVoteState();  
//      }  
         
        // 然後轉調狀態對象來進行相應的操作  
        state.vote(user, voteItem, this); 
    } 

package com.bankht.State.Vote;

import java.util.HashMap;
import java.util.Map;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:41:35
 *
 * @類說明 :環境類
 */
public class VoteManager {
 // 持有狀體處理對象
 private VoteState state = null;
 // 記錄用戶投票的結果,Map<String,String>對應Map<用戶名稱,投票的選項>
 private Map<String, String> mapVote = new HashMap<String, String>();
 // 記錄用戶投票次數,Map<String,Integer>對應Map<用戶名稱,投票的次數>
 private Map<String, Integer> mapVoteCount = new HashMap<String, Integer>();

 /**
  * 獲取用戶投票結果的Map
  */
 public Map<String, String> getMapVote() {
  return mapVote;
 }

 /**
  * 投票
  *
  * @param user
  *            投票人
  * @param voteItem
  *            投票的選項
  */
 public void vote(String user, String voteItem) {
  // 1.為該用戶增加投票次數
  // 從記錄中取出該用戶已有的投票次數
  Integer oldVoteCount = mapVoteCount.get(user);
  if (oldVoteCount == null) {
   oldVoteCount = 0;
  }
  oldVoteCount += 1;
  mapVoteCount.put(user, oldVoteCount);
  // 2.判斷該用戶的投票類型,就相當於判斷對應的狀態
  // 到底是正常投票、重復投票、惡意投票還是上黑名單的狀態
  //下面使用本人自創常用的“假設修正法” 完成switch的結構
  if(oldVoteCount == 1 ) {
   state = new NormalVoteState();
  }
  if(oldVoteCount > 1 ) {
   state = new RepeatVoteState();
  }
  if(oldVoteCount > 5 ) {
   state = new SpiteVoteState();
  }
  if(oldVoteCount > 8 ) {
   state = new BlackVoteState();
  }
  
//  if (oldVoteCount == 1) {
//   state = new NormalVoteState();
//  } else if (oldVoteCount > 1 && oldVoteCount < 5) {
//   state = new RepeatVoteState();
//  } else if (oldVoteCount >= 5 && oldVoteCount < 8) {
//   state = new SpiteVoteState();
//  } else if (oldVoteCount > 8) {
//   state = new BlackVoteState();
//  }
  
  // 然後轉調狀態對象來進行相應的操作
  state.vote(user, voteItem, this);
 }
}
 

 

  客戶端類

[java] 
package com.bankht.State.Vote; 
 
/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:42:06
 * 
 * @類說明 :客戶端類
 */ 
public class Client { 
 
    public static void main(String[] args) { 
 
        VoteManager vm = new VoteManager(); 
        for (int i = 0; i < 11; i++) { 
            vm.vote("u1", "A"); 
        } 
    } 
 

package com.bankht.State.Vote;

/**
 * @author: 特種兵—AK47
 * @創建時間:2012-7-3 上午09:42:06
 *
 * @類說明 :客戶端類
 */
public class Client {

 public static void main(String[] args) {

  VoteManager vm = new VoteManager();
  for (int i = 0; i < 11; i++) {
   vm.vote("u1", "A");
  }
 }

}

 

  運行結果如下:

[html] view plaincopyprint?
恭喜投票成功 
請不要重復投票 
請不要重復投票 
請不要重復投票 
請不要重復投票 
你有惡意刷屏行為,取消投票資格 
你有惡意刷屏行為,取消投票資格 
你有惡意刷屏行為,取消投票資格 
進入黑名單,將禁止登錄和使用本系統 
進入黑名單,將禁止登錄和使用本系統 
進入黑名單,將禁止登錄和使用本系統 
恭喜投票成功
請不要重復投票
請不要重復投票
請不要重復投票
請不要重復投票
你有惡意刷屏行為,取消投票資格
你有惡意刷屏行為,取消投票資格
你有惡意刷屏行為,取消投票資格
進入黑名單,將禁止登錄和使用本系統
進入黑名單,將禁止登錄和使用本系統
進入黑名單,將禁止登錄和使用本系統
 

 

  從上面的示例可以看出,狀態的轉換基本上都是內部行為,主要在狀態模式內部來維護。比如對於投票的人員,任何時候他的操作都是投票,但是投票管理對象的處理卻不一定一樣,會根據投票的次數來判斷狀態,然後根據狀態去選擇不同的處理。

認識狀態模式
  ●  狀態和行為

  所謂對象的狀態,通常指的就是對象實例的屬性的值;而行為指的就是對象的功能,再具體點說,行為大多可以對應到方法上。

  狀態模式的功能就是分離狀態的行為,通過維護狀態的變化,來調用不同狀態對應的不同功能。也就是說,狀態和行為是相關聯的,它們的關系可以描述為:狀態決定行為。

  由於狀態是在運行期被改變的,因此行為也會在運行期根據狀態的改變而改變。

  ●  行為的平行性

  註意平行線而不是平等性。所謂平行性指的是各個狀態的行為所處的層次是一樣的,相互獨立的、沒有關聯的,是根據不同的狀態來決定到底走平行線的哪一條。行為是不同的,當然對應的實現也是不同的,相互之間是不可替換的。

 

  而平等性強調的是可替換性,大傢是同一行為的不同描述或實現,因此在同一個行為發生的時候,可以根據條件挑選任意一個實現來進行相應的處理。

 

  大傢可能會發現狀態模式的結構和策略模式的結構完全一樣,但是,它們的目的、實現、本質卻是完全不一樣的。還有行為之間的特性也是狀態模式和策略模式一個很重要的區別,狀態模式的行為是平行性的,不可相互替換的;而策略模式的行為是平等性的,是可以相互替換的。

  ●   環境和狀態處理對象

  在狀態模式中,環境(Context)是持有狀態的對象,但是環境(Context)自身並不處理跟狀態相關的行為,而是把處理狀態的功能委托給瞭狀態對應的狀態處理類來處理。

  在具體的狀態處理類中經常需要獲取環境(Context)自身的數據,甚至在必要的時候會回調環境(Context)的方法,因此,通常將環境(Context)自身當作一個參數傳遞給具體的狀態處理類。

  客戶端一般隻和環境(Context)交互。客戶端可以用狀態對象來配置一個環境(Context),一旦配置完畢,就不再需要和狀態對象打交道瞭。客戶端通常不負責運行期間狀態的維護,也不負責決定後續到底使用哪一個具體的狀態處理對象。

  作者:m13666368773
 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *