Java/Android 设计模式系列(19)--状态模式
2017-08-28 12:04
633 查看
这篇博客我们来介绍一下状态模式(State Pattern),也是行为型设计模式之一。状态模式的行为是由状态来决定的,不同的状态下有不同的行为。状态模式和策略模式的结构类图几乎完全一样,但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、可相互替换的。状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类;而策略模式可以想象成是除了继承之外的一种弹性替代方案,如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至要修改它都很难,有了策略模式,你可以通过组合不同的对象来改变行为。状态模式的意图是让一个对象在其内部状态发生改变的时候,其行为也随之改变。
状态模式的使用场景:
一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为;
代码中包含大量与状态有关的条件语句,例如,一个操作中含有庞大的多分枝语句(if-else 或者 switch-case),且这些分支依赖于该对象的状态。
状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化,这样通过多态来去除过多的、重复的 if-else 等分支语句。
状态模式的 uml 类图有三个角色:
Context:环境类,定义客户感兴趣的接口,维护一个 State 子类,这个实例定义了对象的当前状态;
State:抽象状态类或者状态接口,定义一个或者一组接口,表示该状态下的行为;
ConcreteStateA、ConcreteStateB:具体状态类,每一个具体的状态类实现抽象的 State 中定义的接口,从而达到不同状态下的不同行为。
据此我们可以写出状态模式的通用代码:
状态接口以及相关子类:
State.class
ConcreteStateA.class、ConcreteStateB.class、NullState.class
Context类以及测试代码:
Context.class
最后结果:
椭圆代表 MediaPlayer 对象可能驻留的状态。弧线表示驱动 MediaPlayer 在各个状态之间迁移的播放控制操作。这里有两种类型的弧线。由一个箭头开始的弧代表同步的方法调用,而以双箭头开头的代表的弧线代表异步方法调用。MediaPlayer在这我就不详细介绍了,网上资料很多,感兴趣的可以去查阅一下。
这里再介绍一下状态机,又称为有限状态自动机 (FSM:Finite State Machine),是表示有限多个状态以及在这些状态之间转移和动作的数学模型。状态存储关于过去的信息,它反映从系统开始到现在时刻输入的变化;转移指示状态变更,用必须满足来确使转移发生的条件来描述它;动作是在给定时刻要进行的活动描述,详细的看看这篇博客:有限状态机(FSM)的Java 演示 。
实际 android 开发过程中,我们一般会去根据实际情况去先构造一个状态图,定义每个状态和每个状态之间切换的事件,类似于上图的 MediaPlayer,然后将该信息录入进入状态机,当目前的状态接收到一个非法的跳转事件时,可以抛出异常,这样就能保证一切按照预先设定好的方向进行。
Statelike.class
StateLowerCase.class 和 StateMultipleUpperCase.class
StateContext.class
最后结果:
例子也很简单,一目了然。
优点:
通过将每个状态封装进一个类,将以后所做的修改局部化;
将所有与一个特定状态相关的行为封装到一个对象中,繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时增加可维护性和可扩展性。
缺点当然也很明显,也是绝大部分设计模式的通病,类数目的增多。
http://blog.csdn.net/jason0539/article/details/45021055
http://blog.csdn.net/shulianghan/article/details/38487967
http://blog.csdn.net/eager7/article/details/8517827
设计模式总目录
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 function this 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"); } }
最后结果:
monday TUESDAY WEDNESDAY thursday FRIDAY SATURDAY sunday
例子也很简单,一目了然。
总结
状态模式的关键点在于不同的状态下对于统一行为有不同的响应,这其实就是一个将 if-else 用多态来实现的一个具体实例。在 if-else 或者 switch-case 形势下根据不同的状态进行判断,如果是状态 A 那么执行方法 A,状态 B 执行方法 B,但这种实现使得逻辑耦合在一起,易于出错不易维护,通过状态模式能够很好的消除这类“丑陋”的逻辑处理,当然并不是任何出现 if-else 的地方都应该通过状态模式重构,模式的运用一定要考虑所处的情景以及你要解决的问题,只有符合特定的场景才建议使用对应的模式。和程序状态机(PSM)不同,状态模式用类代表状态,状态的转换可以由 State 类或者 Context 类控制。优点:
通过将每个状态封装进一个类,将以后所做的修改局部化;
将所有与一个特定状态相关的行为封装到一个对象中,繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时增加可维护性和可扩展性。
缺点当然也很明显,也是绝大部分设计模式的通病,类数目的增多。
源码下载
https://github.com/Jakey-jp/Design-Patterns/tree/master/StatePattern引用
https://en.wikipedia.org/wiki/State_patternhttp://blog.csdn.net/jason0539/article/details/45021055
http://blog.csdn.net/shulianghan/article/details/38487967
http://blog.csdn.net/eager7/article/details/8517827
相关文章推荐
- java/android 设计模式学习笔记(19)---状态模式
- Java/Android 设计模式系列(11)--原型模式
- Java设计模式菜鸟系列(十三)建模和实现状态模式
- Java/Android 设计模式系列(5)--对象池模式
- Java设计模式(19)状态模式(State模式)
- java设计模式系列--状态模式
- Java/Android 设计模式系列(12)--组合模式
- Java/Android 设计模式系列(21)--备忘录模式
- JAVA系列-设计模式-状态模式
- Java/Android 设计模式系列(7)--装饰者模式
- Java/Android 设计模式系列(8)--桥接模式
- Java设计模式系列之状态模式
- Java/Android 设计模式系列--目录
- Java/Android 设计模式系列(20)--迭代器模式
- Java/Android 设计模式系列(4)--抽象工厂模式
- Java/Android 设计模式系列(10)--建造者模式
- Java/Android 设计模式系列(17)--策略模式
- Java/Android 设计模式系列(9)--代理模式
- Java/Android 设计模式系列(3)--工厂方法模式
- Java/Android 设计模式系列(24)--访问者模式