java/android 设计模式学习笔记(19)---状态模式

来源:互联网 发布:算法心得 原书 编辑:程序博客网 时间:2024/04/29 13:29

  这篇博客我们来介绍一下状态模式(State Pattern),也是行为型设计模式之一。状态模式的行为是由状态来决定的,不同的状态下有不同的行为。状态模式和策略模式的结构类图几乎完全一样,但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、可相互替换的。状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类;而策略模式可以想象成是除了继承之外的一种弹性替代方案,如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至要修改它都很难,有了策略模式,你可以通过组合不同的对象来改变行为。状态模式的意图是让一个对象在其内部状态发生改变的时候,其行为也随之改变。
  转载请注明出处:http://blog.csdn.net/self_study/article/details/52432486。
  PS:对技术感兴趣的同鞋加群544645972一起交流。

设计模式总目录

  java/android 设计模式学习笔记目录

特点

  当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
  状态模式的使用场景:

  • 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为;
  • 代码中包含大量与状态有关的条件语句,例如,一个操作中含有庞大的多分枝语句(if-else 或者 switch-case),且这些分支依赖于该对象的状态。
状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化,这样通过多态来去除过多的、重复的 if-else 等分支语句。

UML类图

  这里写图片描述
  状态模式的 uml 类图有三个角色:

  • Context:环境类,定义客户感兴趣的接口,维护一个 State 子类,这个实例定义了对象的当前状态;
  • State:抽象状态类或者状态接口,定义一个或者一组接口,表示该状态下的行为;
  • ConcreteStateA、ConcreteStateB:具体状态类,每一个具体的状态类实现抽象的 State 中定义的接口,从而达到不同状态下的不同行为。
  据此我们可以写出状态模式的通用代码:
状态接口以及相关子类:
State.class

public interface State {    void doSomething();}

ConcreteStateA.class、ConcreteStateB.class、NullState.class

public class ConcreteStateA implements State {    @Override    public void doSomething() {        System.out.print("this is ConcreteStateA's function\n");    }}
public class ConcreteStateB implements State{    @Override    public void doSomething() {        System.out.print("this is ConcreteStateB's function\n");    }}
public class NullState implements State{    @Override    public void doSomething() {        //do nothing    }}

Context类以及测试代码:
Context.class

public class Context {    private State state = new NullState();    void setState(State state) {        this.state = state;    }    void doSomething() {        state.doSomething();    }    public static void main(String[] args) {        Context context = new Context();        context.setState(new ConcreteStateA());        context.doSomething();        context.setState(new ConcreteStateB());        context.doSomething();    }}

最后结果:

this is ConcreteStateA's functionthis is ConcreteStateB's function

示例与源码

  在 Android 源码中也有很多状态模式的例子,MediaPlayer 和 WifiStateEngine 等,这里我们来简单看看 MediaPlayer 的状态机:
  这里写图片描述
椭圆代表 MediaPlayer 对象可能驻留的状态。弧线表示驱动 MediaPlayer 在各个状态之间迁移的播放控制操作。这里有两种类型的弧线。由一个箭头开始的弧代表同步的方法调用,而以双箭头开头的代表的弧线代表异步方法调用。MediaPlayer在这我就不详细介绍了,网上资料很多,感兴趣的可以去查阅一下。
  这里再介绍一下状态机,又称为有限状态自动机 (FSM:Finite State Machine),是表示有限多个状态以及在这些状态之间转移和动作的数学模型。状态存储关于过去的信息,它反映从系统开始到现在时刻输入的变化;转移指示状态变更,用必须满足来确使转移发生的条件来描述它;动作是在给定时刻要进行的活动描述,详细的看看这篇博客:有限状态机(FSM)的Java 演示 。
  实际 Android 开发过程中,我们一般会去根据实际情况去先构造一个状态图,定义每个状态和每个状态之间切换的事件,类似于上图的 MediaPlayer,然后将该信息录入进入状态机,当目前的状态接收到一个非法的跳转事件时,可以抛出异常,这样就能保证一切按照预先设定好的方向进行。

Demo

  我们这里仍然以 wiki 上的 demo 为例,来实现一堆字符串的一个大小写间隔打印:
Statelike.class

interface Statelike {    void writeName(StateContext context, String name);}

StateLowerCase.class 和 StateMultipleUpperCase.class

class StateLowerCase implements Statelike {    @Override    public void writeName(final StateContext context, final String name) {        System.out.println(name.toLowerCase());        context.setState(new StateMultipleUpperCase());    }}
class StateMultipleUpperCase implements Statelike {    /** Counter local to this state */    private int count = 0;    @Override    public void writeName(final StateContext context, final String name) {        System.out.println(name.toUpperCase());        /* Change state after StateMultipleUpperCase's writeName() gets invoked twice */        if(++count > 1) {            context.setState(new StateLowerCase());        }    }}

StateContext.class

class StateContext {    private Statelike myState;    StateContext() {        setState(new StateLowerCase());    }    /**     * Setter method for the state.     * Normally only called by classes implementing the State interface.     * @param newState the new state of this context     */    void setState(final Statelike newState) {        myState = newState;    }    public void writeName(final String name) {        myState.writeName(this, name);    }    public static void main(String[] args) {        final StateContext sc = new StateContext();        sc.writeName("Monday");        sc.writeName("Tuesday");        sc.writeName("Wednesday");        sc.writeName("Thursday");        sc.writeName("Friday");        sc.writeName("Saturday");        sc.writeName("Sunday");    }}

最后结果:

mondayTUESDAYWEDNESDAYthursdayFRIDAYSATURDAYsunday

例子也很简单,一目了然。

总结

  状态模式的关键点在于不同的状态下对于统一行为有不同的响应,这其实就是一个将 if-else 用多态来实现的一个具体实例。在 if-else 或者 switch-case 形势下根据不同的状态进行判断,如果是状态 A 那么执行方法 A,状态 B 执行方法 B,但这种实现使得逻辑耦合在一起,易于出错不易维护,通过状态模式能够很好的消除这类“丑陋”的逻辑处理,当然并不是任何出现 if-else 的地方都应该通过状态模式重构,模式的运用一定要考虑所处的情景以及你要解决的问题,只有符合特定的场景才建议使用对应的模式。和程序状态机(PSM)不同,状态模式用类代表状态,状态的转换可以由 State 类或者 Context 类控制。
  优点:

  • 通过将每个状态封装进一个类,将以后所做的修改局部化;
  • 将所有与一个特定状态相关的行为封装到一个对象中,繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时增加可维护性和可扩展性。
缺点当然也很明显,也是绝大部分设计模式的通病,类数目的增多。

源码下载

  https://github.com/zhaozepeng/Design-Patterns/tree/master/StatePattern

引用

https://en.wikipedia.org/wiki/State_pattern
http://blog.csdn.net/jason0539/article/details/45021055
http://blog.csdn.net/shulianghan/article/details/38487967
http://blog.csdn.net/eager7/article/details/8517827

1 2
原创粉丝点击