打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
状态机模式

现在需要你做一个简单是视频播放器的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接口了:

 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     @Override21     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     @Override39     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需要在视频播放中(片头或者片尾什么的)可以播放一段广告。嗯,你可能会觉得没关系,只需要在接口上增加多一个方法就好了,同时增加个状态字段,修改后:

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类,我们继续以视频播放为例子:

首先还是实现播放,暂停,停止状态,此时的状态转换图应该是这样:


 

 

 

 

 

 

 

 

 

 

 

 

还是先抽象一个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     @Override10     public String toString() {11         return "current state:"+this.getClass().getSimpleName();12     }13 }

 

再看State的实现,我们有播放,暂停,停止三种状态,所以需要三个实现类:

PlayingState
PausedState
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的操作就可以了,剩下的就是状态们之间的事了。

@Override    public 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行的代码。

再来验证程序:

同样可以正确的运行。也可以看出,对于状态的增加,所带来的修改成本比没用状态机模式要小的多,特别对于状态更多的程序。

至此状态机模式也讲完了。

总结:

1.状态机模式:允许对象在内部状态改变时改变它的行为,对象看起来就好像修改了它的类(每个状态可以做出不一样的动作);

2.拥有多个状态的对象(Context)只需要实现需要的操作,每次接收输入的时候(request方法调用),只需要交给当前的状态去处理,而每个状态不需要知道自己之前的状态是什么,只需要知道接收到什么样的输入(或者没输入)而做出相应的操作和自己下一个状态是什么即可;

3.适当的画出系统的状态转换图,可以更清晰地实现系统状态机。  

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
状态模式
1.Android基础篇 ? { Android学习指南 }
《JAVA与模式》之状态模式
行为型模式:状态模式
11. 状态模式
《Head First设计模式》阅读笔记.第十章
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服