标签:
现在需要你做一个简单是视频播放器的APP,主要有播放,暂停,停止三个功能,在没学状态机模式之前,你可能会这样来实现:
现抽象个IPlayer接口,定义好你的播放器需要实现的动作和可能的状态字段:
01.1 public interface IPlayer {02.2 public static final int STATE_PLAYING = 1;03.3 public static final int STATE_PAUSED = 2;04.4 public static final int STATE_STOPPED = 3;05.506.6 public void palyVedio();07.708.8 public void pause();09.910.10 public void stop();11.11 }现在就可以实现IPlayer接口了:
01.1 public class VedioPlayer implements IPlayer {02.2 public int mCurrentState;03.304.4 @Override05.5 public void palyVedio() {06.6 switch (mCurrentState) {07.7 case STATE_PLAYING:08.8 System.out.println(‘ curent state is palying, do nothing.‘);09.9 case STATE_PAUSED:10.10 case STATE_STOPPED:11.11 System.out.println(‘paly vedio now.‘);12.12 break;13.13 default:14.14 // would it happen? who care.15.15 break;16.16 }17.17 mCurrentState = STATE_PLAYING;18.18 }19.1920.20 @Override21.21 public void pause() {22.22 switch (mCurrentState) {23.23 case STATE_PLAYING:24.24 System.out.println(‘pause vedio now‘);25.25 break;26.26 case STATE_PAUSED:27.27 System.out.println(‘ curent state is paused, do noting.‘);28.28 case STATE_STOPPED:29.29 System.out.println(‘curent state is stopped,do noting.‘);30.30 break;31.31 default:32.32 // would it happen? who care.33.33 break;34.34 }35.35 mCurrentState = STATE_PAUSED;36.36 }37.3738.38 @Override39.39 public void stop() {40.40 switch (mCurrentState) {41.41 case STATE_PLAYING:42.42 case STATE_PAUSED:43.43 System.out.println(‘ stop vedio now.‘);44.44 case STATE_STOPPED:45.45 System.out.println(‘curent state is stopped,do noting.‘);46.46 break;47.47 default:48.48 // would it happen? who care.49.49 break;50.50 }51.51 mCurrentState = STATE_STOPPED;52.52 }53.5354.5455.55 }看着还错喔。
我们都知道,需求总是会改变的,现在你的boss需要在视频播放中(片头或者片尾什么的)可以播放一段广告。嗯,你可能会觉得没关系,只需要在接口上增加多一个方法就好了,同时增加个状态字段,修改后:
01.1 public interface IPlayer {02.2 public static final int STATE_PLAYING = 1;03.3 public static final int STATE_PAUSED = 2;04.4 public static final int STATE_STOPPED = 3;05.5 public static final int STATE_AD = 4;06.6 07.7 public void palyVedio();08.8 public void pause();09.9 public void stop();10.10 public void showAD();11.11 }最后你认为只需要VedioPlayer实现增加的showAD方法就大功告成了,
01.1 @Override02.2 public void showAD() {03.3 switch (mCurrentState) {04.4 case STATE_AD:05.5 System.out.println(‘curent state is AD,do noting‘);06.6 break;07.7 case STATE_PLAYING:08.8 System.out.println(‘show advertisement now.‘);09.9 break;10.10 case STATE_PAUSED:11.11 System.out.println(‘curent state is paused , do noting‘);12.12 case STATE_STOPPED:13.13 System.out.println(‘curent state is stopped ,do noting.‘);14.14 break;15.15 default:16.16 // would it happen? who care.17.17 break;18.18 }19.19 mCurrentState = STATE_AD;20.20 }真的就完了?终于发现了,palyVedio,pause,stop三个方法中的swtich里面还需要各多加一个case的判断,纳尼!!!如果以后又增加几个状态,那么还得修改啊,而且随着状态的增加,修改的代码也会成倍的增加,简直不可想象。这种情况下,状态机模式就可以帮你个大忙了。
状态机模式:允许对象在内部状态改变时改变它的行为,对象看起来就好像修改了它的类。
看着还是有点抽象吧,这里的Context就相当于我们的VedioPlayer类,我们继续以视频播放为例子:
首先还是实现播放,暂停,停止状态,此时的状态转换图应该是这样:

还是先抽象一个IPlayer作为上下文(Context):
01.1 public abstract class IPlayer {02.2 03.3 public abstract void request(int flag);04.4 05.5 public abstract void setState(PlayerState state);06.6 07.7 public abstract void palyVedio();08.809.9 public abstract void pause();10.1011.11 public abstract void stop();12.1213.13 public abstract void showAD();14.14 }可以看到有一个setState方法,这是为了可以设置内部状态。
有了Context,我来实现State吧,这里写成一个抽线类
01.1 public abstract class PlayerState {02.2 public final static int PLAY_OR_PAUSE=0;03.3 public final static int STOP=1;04.4 protected IPlayer mPlayer;05.5 public PlayerState(IPlayer player) {06.6 this.mPlayer=player;07.7 }08.8 public abstract void handle(int action);09.9 @Override10.10 public String toString() {11.11 return ‘current state:‘+this.getClass().getSimpleName();12.12 }13.13 }再看State的实现,我们有播放,暂停,停止三种状态,所以需要三个实现类:
01.public class PlayingState extends PlayerState {02.public PlayingState(IPlayer player) {03.super(player);04.}05. 06.@Override07.public void handle(int action) {08.switch (action) {09.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.default:18.throw new IllegalArgumentException(‘ERROE ACTION:‘+action+‘,current state:‘+this.getClass().getSimpleName());19.}20.}21.}
01.public class PausedState extends PlayerState {02. 03.public PausedState(IPlayer player) {04.super(player);05.}06.@Override07.public void handle(int action) {08.switch (action) {09.case PlayingState.PLAY_OR_PAUSE:10.mPlayer.palyVedio();11.mPlayer.setState(new PlayingState(mPlayer));12.break;13.case PlayerState.STOP:14.mPlayer.stop();15.mPlayer.setState(new StoppedState(mPlayer));16.break;17.default:18.throw new IllegalArgumentException(‘ERROE ACTION:‘+action+‘,current state:‘+this.getClass().getSimpleName());19.}20.}21.}
01.public class StoppedState extends PlayerState {02. 03.public StoppedState(IPlayer player) {04.super(player);05.}06. 07.@Override08.public void handle(int action) {09.switch (action) {10.case PlayingState.PLAY_OR_PAUSE:11.mPlayer.palyVedio();12.mPlayer.setState(new PlayingState(mPlayer));13.break;14.default:15.throw new IllegalArgumentException(‘ERROE ACTION:‘+action+‘,current state:‘+this.getClass().getSimpleName());16.}17.}18.}最后就是IPlayer的实现类VedioPlayer
01.public class VedioPlayer extends IPlayer {02.private PlayerState mState=new StoppedState(this);03. 04.@Override05.public void palyVedio() {06.System.out.println(‘play vedio!‘);07.}08. 09.@Override10.public void pause() {11.System.out.println(‘pause vedio!‘);12.}13. 14.@Override15.public void stop() {16.System.out.println(‘stop vedio!‘);17.}18. 19.// @Override20.// public void showAD() {21.// System.out.println(‘show AD!‘);22.// }23. 24.@Override25.public void setState(PlayerState state) {26.mState = state;27.}28. 29.@Override30.public void request(int action) {31.System.out.println(‘before action:‘ + mState.toString());32.mState.handle(action);33.System.out.println(‘after action:‘ + mState.toString());34.}35. 36.}现在的代码就简洁多了,因为VedioPlayer只需要实现需要的操作,每次接收输入的时候(request方法调用),只需要交给当前的状态去处理,而每个状态不需要知道自己之前的状态是什么,只需要知道接收到什么样的输入而做出相应的操作和下一个状态,现在来验证下正确性:
01.1 public class Main {02.203.3 /**04.4 * @param args05.5 */06.6 public static void main(String[] args) {07.7 Scanner sc=new Scanner(System.in);08.8 IPlayer player=new VedioPlayer();09.9 int i=-1;10.10 while((i=sc.nextInt())!=-1){11.11 player.request(i);12.12 }13.13 }14.1415.15 }依次如下输入:

最后抛出了java.lang.IllegalArgumentException: ERROE ACTION:1,current state:StoppedState,因为在stopped状态下,又再次尝试stop,具体可以看StoppedState的实现。从流程来看,也验证了程序的正确性。
现在我们为视频播放器添加一个播放广告的状态,此时系统的状态:

上面我们提到VedioPlayer只需要实现需要的操作,每次接收输入的时候(request方法调用),只需要交给当前的状态去处理。
也就是说现在的VedioPlayer再实现一个showAD的操作就可以了,剩下的就是状态们之间的事了。
1.@Override2.public void showAD() {3.System.out.println(‘show AD!‘);4.}现在增加一个ADState
01.public class ShowADState extends PlayerState {02.public ShowADState(IPlayer player) {03.super(player);04.}05.@Override06.public void handle(int action) {07.switch (action) {08.case PlayingState.PLAY_OR_PAUSE:09.mPlayer.palyVedio();10.mPlayer.setState(new PlayingState(mPlayer));11.break;12.default:13.throw new IllegalArgumentException(‘ERROE ACTION:‘+action+‘,‘+this.toString());14.}15.}16. 17.}现在依然还没有完事,前面提到,每个状态不需要知道自己之前的状态是什么,只需要知道接收到什么样的输入而做出相应的操作和下一个状态。
由状态图可以看到,PlayingState的下一个状态增加了一个ShowADState,所以PlayingState还需要做一点修改,如下:
01.1 public class PlayingState extends PlayerState {02.2 public PlayingState(IPlayer player) {03.3 super(player);04.4 }05.506.6 @Override07.7 public void handle(int action) {08.8 switch (action) {09.9 case PlayingState.PLAY_OR_PAUSE:10.10 mPlayer.pause();11.11 mPlayer.setState(new PausedState(mPlayer));12.12 break;13.13 case PlayerState.STOP:14.14 mPlayer.stop();15.15 mPlayer.setState(new StoppedState(mPlayer));16.16 break;17.17 case PlayingState.SHOW_AD:18.18 mPlayer.showAD();19.19 mPlayer.setState(new ShowADState(mPlayer));20.20 break;21.21 default:22.22 throw new IllegalArgumentException(‘ERROE ACTION:‘+action+‘,current state:‘+this.getClass().getSimpleName());23.23 }24.24 }25.25 }增加了17到20行的代码。
再来验证程序:

同样可以正确的运行。也可以看出,对于状态的增加,所带来的修改成本比没用状态机模式要小的多,特别对于状态更多的程序。
至此状态机模式也讲完了。
总结:
1.状态机模式:允许对象在内部状态改变时改变它的行为,对象看起来就好像修改了它的类(每个状态可以做出不一样的动作);
2.拥有多个状态的对象(Context)只需要实现需要的操作,每次接收输入的时候(request方法调用),只需要交给当前的状态去处理,而每个状态不需要知道自己之前的状态是什么,只需要知道接收到什么样的输入(或者没输入)而做出相应的操作和自己下一个状态是什么即可;
3.适当的画出系统的状态转换图,可以更清晰地实现系统状态机。
标签:
原文地址:http://www.cnblogs.com/zhangchenliang/p/4951232.html