java設計模式-----22、狀態模式


  概念:

  State模式也叫狀態模式,是行為設計模式的一種。State模式允許通過改變對象的內部狀態而改變對象的行為,這個對象表現得就好像修改了它的類一樣。

  根據這個概念,我們舉個例子

 1 public class Behavior {
 2     private int time;
 3 
 4     public int getTime() {
 5         return time;
 6     }
 7 
 8     public void setTime(int time) {
 9         this.time = time;
10     }
11     
12     public void eat(){
13         if(time == 7){
14             System.out.println("吃早飯");
15         }else if(time == 12){
16             System.out.println("吃午飯");
17         }else if(time == 18){
18             System.out.println("吃晚飯");
19         }else{
20             System.out.println("還不到吃飯時間");
21         }
22     }
23 }
 1 public class MainClass {
 2     public static void main(String[] args) {
 3         Behavior behavior = new Behavior();
 4         behavior.setTime(7);
 5         behavior.eat();
 6         
 7         behavior.setTime(12);
 8         behavior.eat();
 9         
10         behavior.setTime(18);
11         behavior.eat();
12         
13         behavior.setTime(20);
14         behavior.eat();
15     }
16 }

   結果:

  可以看到,根據time屬性的不同,對象的行為也發生了改變,但是這樣的方式很不好,所有的事情都放到了eat()方法中,導致eat()方法過於復雜

  下面就看一看狀態模式

  狀態模式的應用場景

  狀態模式主要解決的是當控制一個對象狀態轉換的條件表達式過於復雜時的情況。把狀態的判斷邏輯轉譯到表現不同狀態的一系列類當中,可以把復雜的判斷邏輯簡化。

  簡單來說:

  1.一個對象的行為取決於它的狀態,並且它必須在運行時刻根據狀態改變它的行為。
  2.一個操作中含有龐大的多分支結構,並且這些分支決定於對象的狀態。

  狀態模式的結構

 

  

  狀態模式的角色和職責

  1、Context:用戶對象:擁有一個State類型的成員,以標識對象的當前狀態(Behavior)

  2、State:接口或基類封裝與Context的特定狀態相關的行為;

  3、ConcreteState:接口實現類或子類實現了一個與Context某個狀態相關的行為。

   按照狀態模式,我們來改造一下,剛才的例子,吃早中晚飯,不是吃飯時間,都是狀態,所以我們把狀態單獨封裝出來。

  首先,新建一個State

1 public abstract class State {
2     public abstract void eat();
3 }

  接着新建ConcreteState

 1 /*
 2  * 早餐
 3  */
 4 public class BreakfastState extends State {
 5 
 6     @Override
 7     public void eat() {
 8         System.out.println("吃早餐");
 9     }
10 
11 }
 1 /*
 2  * 午餐
 3  */
 4 public class LunchState extends State {
 5 
 6     @Override
 7     public void eat() {
 8         System.out.println("吃午餐");
 9     }
10 
11 }
 1 /*
 2  * 晚餐
 3  */
 4 public class DinnerState extends State {
 5 
 6     @Override
 7     public void eat() {
 8         System.out.println("吃晚餐");
 9     }
10 
11 }
 1 /*
 2  * 不是吃飯時間
 3  */
 4 public class NoFoodState extends State {
 5 
 6     @Override
 7     public void eat() {
 8         System.out.println("不是吃飯時間");
 9     }
10 
11 }

  再修改一下behavior

 1 public class Behavior {
 2     private int time;
 3     State state  = null;
 4 
 5     public int getTime() {
 6         return time;
 7     }
 8 
 9     public void setTime(int time) {
10         this.time = time;
11     }
12     
13     public void eat(){
14         if(time == 7){
15             state = new BreakfastState();
16             state.eat();
17         }else if(time == 12){
18             state = new LunchState();
19             state.eat();
20         }else if(time == 18){
21             state = new DinnerState();
22             state.eat();
23         }else{
24             state = new NoFoodState();
25             state.eat();
26         }
27     }
28 }

  這樣,和剛才的結果一樣,但是這樣子,判斷邏輯還是在對象中,我們繼續修改,將邏輯寫到ConcreteState

  因為,我們要知道time,所以需要向state中傳入參數,所以我們將Behavior傳進去

1 public abstract class State {
2     public abstract void eat(Behavior behavior);
3 }

  然后,修改Behavior

 1 public class Behavior {
 2     private int time;
 3     State state  = null;
 4 
 5     public Behavior() {
 6         state = new BreakfastState();
 7     }
 8 
 9     public int getTime() {
10         return time;
11     }
12 
13     public void setTime(int time) {
14         this.time = time;
15     }
16     
17     public State getState() {
18         return state;
19     }
20 
21     public void setState(State state) {
22         this.state = state;
23     }
24 
25     public void eat(){
26         //邏輯取出,所以這里只剩調用方法
27         state.eat(this);
28         //當所有方法都完成后,回到最初始狀態
29         state = new BreakfastState();
30     }
31 }

   接着,再繼續修改每一個ConcreteState

 1 /*
 2  * 早餐
 3  */
 4 public class BreakfastState extends State {
 5 
 6     @Override
 7     public void eat(Behavior behavior) {
 8         if(behavior.getTime() == 7){
 9             System.out.println("吃早餐");
10         }else{
11             //如果不符合條件,重置state為下一個狀態
12             behavior.setState(new LunchState());
13             behavior.eat();
14         }
15     }
16 }
 1 /*
 2  * 午餐
 3  */
 4 public class LunchState extends State {
 5 
 6     @Override
 7     public void eat(Behavior behavior) {
 8         if(behavior.getTime() == 12){
 9             System.out.println("吃午餐");
10         }else{
11             behavior.setState(new DinnerState());
12             behavior.eat();
13         }
14     }
15 
16 }
 1 /*
 2  * 晚餐
 3  */
 4 public class DinnerState extends State {
 5 
 6     @Override
 7     public void eat(Behavior behavior) {
 8         if(behavior.getTime() == 18){
 9             System.out.println("吃晚餐");
10         }else{
11             behavior.setState(new NoFoodState());
12             behavior.eat();
13         }
14     }
15 
16 }
 1 /*
 2  * 不是吃飯時間
 3  */
 4 public class NoFoodState extends State {
 5 
 6     @Override
 7     public void eat(Behavior behavior) {
 8         System.out.println("不是吃飯時間");
 9     }
10 
11 }

   這樣,結果和之前是一樣的

  狀態模式的優點與缺點

  優點: 1、封裝了轉換規則。

      2、枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。

      3、將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。

      4、允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。

      5、可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。

  缺點: 1、狀態模式的使用必然會增加系統類和對象的個數。

      2、狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂。

        3、狀態模式對"開閉原則"的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的源代碼。

  注意事項:在行為受狀態約束的時候使用狀態模式,而且狀態不超過 5 個。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM