狀態機的三個基本要素,狀態機模式

 2023-12-25 阅读 226 评论 0

摘要:? 現在需要你做一個簡單是視頻播放器的APP,主要有播放,暫停,停止三個功能,在沒學狀態機模式之前,你可能會這樣來實現: 現抽象個IPlayer接口,定義好你的播放器需要實現的動作和可能的狀態字段: 1 public interface IP

?

現在需要你做一個簡單是視頻播放器的APP,主要有播放,暫停,停止三個功能,在沒學狀態機模式之前,你可能會這樣來實現:

現抽象個IPlayer接口,定義好你的播放器需要實現的動作和可能的狀態字段:

 1 public interface IPlayer {
 2     public static final int STATE_PLAYING = 1;  3 public static final int STATE_PAUSED = 2;  4 public static final int STATE_STOPPED = 3;  5  6 public void palyVedio();  7  8 public void pause();  9 10 public void stop(); 11 }
IPlayer

現在就可以實現IPlayer接口了:

復制代碼
 1 public class VedioPlayer implements IPlayer {
 2 public int mCurrentState;  3  4  @Override  5 public void palyVedio() {  6 switch (mCurrentState) {  7 case STATE_PLAYING:  8 System.out.println(" curent state is palying, do nothing.");  9 case STATE_PAUSED: 10 case STATE_STOPPED: 11 System.out.println("paly vedio now."); 12 break; 13 default: 14 // would it happen? who care. 15 break; 16  } 17 mCurrentState = STATE_PLAYING; 18  } 19 20  @Override 21 public void pause() { 22 switch (mCurrentState) { 23 case STATE_PLAYING: 24 System.out.println("pause vedio now"); 25 break; 26 case STATE_PAUSED: 27 System.out.println(" curent state is paused, do noting."); 28 case STATE_STOPPED: 29 System.out.println("curent state is stopped,do noting."); 30 break; 31 default: 32 // would it happen? who care. 33 break; 34  } 35 mCurrentState = STATE_PAUSED; 36  } 37 38  @Override 39 public void stop() { 40 switch (mCurrentState) { 41 case STATE_PLAYING: 42 case STATE_PAUSED: 43 System.out.println(" stop vedio now."); 44 case STATE_STOPPED: 45 System.out.println("curent state is stopped,do noting."); 46 break; 47 default: 48 // would it happen? who care. 49 break; 50  } 51 mCurrentState = STATE_STOPPED; 52  } 53 54 55 }
復制代碼

?

狀態機的三個基本要素。看著還錯喔。

我們都知道,需求總是會改變的,現在你的boss需要在視頻播放中(片頭或者片尾什么的)可以播放一段廣告。嗯,你可能會覺得沒關系,只需要在接口上增加多一個方法就好了,同時增加個狀態字段,修改后:

復制代碼
 1 public interface IPlayer {
 2     public static final int STATE_PLAYING = 1;  3 public static final int STATE_PAUSED = 2;  4 public static final int STATE_STOPPED = 3;  5 public static final int STATE_AD = 4;  6  7 public void palyVedio();  8 public void pause();  9 public void stop(); 10 public void showAD(); 11 }
復制代碼
IPlayer

最后你認為只需要VedioPlayer實現增加的showAD方法就大功告成了,

復制代碼
 1     @Override
 2     public void showAD() {  3 switch (mCurrentState) {  4 case STATE_AD:  5 System.out.println("curent state is AD,do noting");  6 break;  7 case STATE_PLAYING:  8 System.out.println("show advertisement now.");  9 break; 10 case STATE_PAUSED: 11 System.out.println("curent state is paused , do noting"); 12 case STATE_STOPPED: 13 System.out.println("curent state is stopped ,do noting."); 14 break; 15 default: 16 // would it happen? who care. 17 break; 18  } 19 mCurrentState = STATE_AD; 20 }
復制代碼

真的就完了?終于發現了,palyVedio,pause,stop三個方法中的swtich里面還需要各多加一個case的判斷,納尼!!!如果以后又增加幾個狀態,那么還得修改啊,而且隨著狀態的增加,修改的代碼也會成倍的增加,簡直不可想象。這種情況下,狀態機模式就可以幫你個大忙了。

狀態機模式:允許對象在內部狀態改變時改變它的行為,對象看起來就好像修改了它的類。

看著還是有點抽象吧,這里的Context就相當于我們的VedioPlayer類,我們繼續以視頻播放為例子:

狀態機主要用于解決什么問題,首先還是實現播放,暫停,停止狀態,此時的狀態轉換圖應該是這樣:

?

?

?

?

java狀態機應用場景、?

?

?

?

?

?

狀態機的概念,?

?

還是先抽象一個IPlayer作為上下文(Context):

復制代碼
 1 public abstract class IPlayer {
 2  3 public abstract void request(int flag);  4  5 public abstract void setState(PlayerState state);  6  7 public abstract void palyVedio();  8  9 public abstract void pause(); 10 11 public abstract void stop(); 12 13 public abstract void showAD(); 14 }
復制代碼

可以看到有一個setState方法,這是為了可以設置內部狀態。

有了Context,我來實現State吧,這里寫成一個抽線類

復制代碼
 1 public abstract class PlayerState {
 2 public final static int PLAY_OR_PAUSE=0;  3 public final static int STOP=1;  4 protected IPlayer mPlayer;  5 public PlayerState(IPlayer player) {  6 this.mPlayer=player;  7  }  8 public abstract void handle(int action);  9  @Override 10 public String toString() { 11 return "current state:"+this.getClass().getSimpleName(); 12  } 13 }
復制代碼

?

狀態機編程思路及方法,再看State的實現,我們有播放,暫停,停止三種狀態,所以需要三個實現類:

復制代碼
public class PlayingState extends PlayerState {public PlayingState(IPlayer player) { super(player); } @Override public void handle(int action) { switch (action) { case PlayingState.PLAY_OR_PAUSE: mPlayer.pause(); mPlayer.setState(new PausedState(mPlayer)); break; case PlayerState.STOP: mPlayer.stop(); mPlayer.setState(new StoppedState(mPlayer)); break; default: throw new IllegalArgumentException("ERROE ACTION:"+action+",current state:"+this.getClass().getSimpleName()); } } }
復制代碼
PlayingState
復制代碼
public class PausedState extends PlayerState {public PausedState(IPlayer player) { super(player); } @Override public void handle(int action) { switch (action) { case PlayingState.PLAY_OR_PAUSE: mPlayer.palyVedio(); mPlayer.setState(new PlayingState(mPlayer)); break; case PlayerState.STOP: mPlayer.stop(); mPlayer.setState(new StoppedState(mPlayer)); break; default: throw new IllegalArgumentException("ERROE ACTION:"+action+",current state:"+this.getClass().getSimpleName()); } } }
復制代碼
PausedState
復制代碼
public class StoppedState extends PlayerState {public StoppedState(IPlayer player) { super(player); } @Override public void handle(int action) { switch (action) { case PlayingState.PLAY_OR_PAUSE: mPlayer.palyVedio(); mPlayer.setState(new PlayingState(mPlayer)); break; default: throw new IllegalArgumentException("ERROE ACTION:"+action+",current state:"+this.getClass().getSimpleName()); } } }
復制代碼
StoppedState

最后就是IPlayer的實現類VedioPlayer

復制代碼
public class VedioPlayer extends IPlayer {private PlayerState mState=new StoppedState(this); @Override public void palyVedio() { System.out.println("play vedio!"); } @Override public void pause() { System.out.println("pause vedio!"); } @Override public void stop() { System.out.println("stop vedio!"); } // @Override // public void showAD() { // System.out.println("show AD!"); // }  @Override public void setState(PlayerState state) { mState = state; } @Override public void request(int action) { System.out.println("before action:" + mState.toString()); mState.handle(action); System.out.println("after action:" + mState.toString()); } }
復制代碼

現在的代碼就簡潔多了,因為VedioPlayer只需要實現需要的操作,每次接收輸入的時候(request方法調用),只需要交給當前的狀態去處理,而每個狀態不需要知道自己之前的狀態是什么,只需要知道接收到什么樣的輸入而做出相應的操作和下一個狀態,現在來驗證下正確性:

復制代碼
 1 public class Main {
 2 
 3 /**  4  * @param args  5 */  6 public static void main(String[] args) {  7 Scanner sc=new Scanner(System.in);  8 IPlayer player=new VedioPlayer();  9 int i=-1; 10 while((i=sc.nextInt())!=-1){ 11  player.request(i); 12  } 13  } 14 15 }
復制代碼

依次如下輸入:

?

狀態機是什么意思,最后拋出了java.lang.IllegalArgumentException: ERROE ACTION:1,current state:StoppedState,因為在stopped狀態下,又再次嘗試stop,具體可以看StoppedState的實現。從流程來看,也驗證了程序的正確性。

現在我們為視頻播放器添加一個播放廣告的狀態,此時系統的狀態:

  上面我們提到VedioPlayer只需要實現需要的操作,每次接收輸入的時候(request方法調用),只需要交給當前的狀態去處理。

  也就是說現在的VedioPlayer再實現一個showAD的操作就可以了,剩下的就是狀態們之間的事了。

@Overridepublic void showAD() {System.out.println("show AD!");}

  現在增加一個ADState

復制代碼
public class ShowADState extends PlayerState {public ShowADState(IPlayer player) { super(player); } @Override public void handle(int action) { switch (action) { case PlayingState.PLAY_OR_PAUSE: mPlayer.palyVedio(); mPlayer.setState(new PlayingState(mPlayer)); break; default: throw new IllegalArgumentException("ERROE ACTION:"+action+","+this.toString()); } } }
復制代碼

狀態機圖怎么畫,現在依然還沒有完事,前面提到,每個狀態不需要知道自己之前的狀態是什么,只需要知道接收到什么樣的輸入而做出相應的操作和下一個狀態。

由狀態圖可以看到,PlayingState的下一個狀態增加了一個ShowADState,所以PlayingState還需要做一點修改,如下:

?

復制代碼
 1 public class PlayingState extends PlayerState {
 2 public PlayingState(IPlayer player) {  3 super(player);  4  }  5  6  @Override  7 public void handle(int action) {  8 switch (action) {  9 case PlayingState.PLAY_OR_PAUSE: 10  mPlayer.pause(); 11 mPlayer.setState(new PausedState(mPlayer)); 12 break; 13 case PlayerState.STOP: 14  mPlayer.stop(); 15 mPlayer.setState(new StoppedState(mPlayer)); 16 break; 17 case PlayingState.SHOW_AD: 18  mPlayer.showAD(); 19 mPlayer.setState(new ShowADState(mPlayer)); 20 break; 21 default: 22 throw new IllegalArgumentException("ERROE ACTION:"+action+",current state:"+this.getClass().getSimpleName()); 23  } 24  } 25 }
復制代碼

增加了17到20行的代碼。

再來驗證程序:

Plcopen狀態機,同樣可以正確的運行。也可以看出,對于狀態的增加,所帶來的修改成本比沒用狀態機模式要小的多,特別對于狀態更多的程序。

至此狀態機模式也講完了。

總結:

1.狀態機模式:允許對象在內部狀態改變時改變它的行為,對象看起來就好像修改了它的類(每個狀態可以做出不一樣的動作);

2.擁有多個狀態的對象(Context)只需要實現需要的操作,每次接收輸入的時候(request方法調用),只需要交給當前的狀態去處理,而每個狀態不需要知道自己之前的狀態是什么,只需要知道接收到什么樣的輸入(或者沒輸入)而做出相應的操作和自己下一個狀態是什么即可;

3.適當的畫出系統的狀態轉換圖,可以更清晰地實現系統狀態機。

狀態機分為哪兩種。轉載于:https://www.cnblogs.com/firstdream/p/7088133.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/195024.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息