定义

允许一个对象在其内部状态改变是改变它的行为。对象看起来似乎修改了它的类。

结构和说明

行为型模式-状态模式 - 图1

示例代码

  1. public class StateDemo {
  2. /**
  3. * 封装与 Context 的一个特定状态相关的行为
  4. */
  5. public static interface State {
  6. /**
  7. * 状态的处理
  8. *
  9. * @param sampleParameter
  10. * 示例参数,说明可以传入参数,具体传入什么样的参数,插入几个参数,由具体应用来具体分析
  11. */
  12. void handler(String sampleParameter);
  13. }
  14. /**
  15. * 实现一个与 Context 的一个特定状态相关的行为
  16. */
  17. public static class ConcreteStateA implements State {
  18. @Override
  19. public void handler(String sampleParameter) {
  20. // 实现具体的处理
  21. }
  22. }
  23. public static class ConcreteStateB implements State {
  24. @Override
  25. public void handler(String sampleParameter) {
  26. }
  27. }
  28. /**
  29. * 定义客户感兴趣的接口,通常会维护一个 State 类型的对象实例
  30. */
  31. public static class Context {
  32. @Setter
  33. private State state;
  34. /**
  35. * 用户感兴趣的接口方法
  36. *
  37. * @param sampleParameter
  38. * 示意参数
  39. */
  40. public void request(String sampleParameter) {
  41. // 在处理中,会转调 State 来处理
  42. state.handler(sampleParameter);
  43. }
  44. }
  45. }

调用顺序

在 Context 中进行状态维护和转换

行为型模式-状态模式 - 图2

状态对象来维护和转换状态

行为型模式-状态模式 - 图3

如何选择?

  • 如果转换的规则是一定的,一般不需要进行什么扩展规则,那么就合适在上下文中统一进行状态的维护。
  • 如果状态的转换取决于前一个状态动态处理的结果,或者是依赖于外部数据,为了增强灵活性,这种情况下,一般是在状态处理类中进行状态的维护。

优缺点

优点

  • 简化应用逻辑控制

状态模式使用单独的一个类封装一个状态的处理。如果把一个大的程序分成很多小块,每块定义一个状态来代表,那么就可以把这些逻辑控制的代码分散到很多单独的状态类中去,这样就把着眼点从执行状态提高到整个对象的状态,使得代码结构化和意图更清晰,从而简化应用的逻辑控制。
对于赖赖状态的 if-else,理论上来讲,也可以改变应用状态模式来实现,把每个 if 或 else 块定义一个状态来代表,那么就可以把块内的功能代码移动到状态处理类中,从而减少 if-else,避免出现巨大的条件语句。

  • 更好地分离状态和行为

状态模式通过设置所有的状态类的公共接口,把状态和状态对应行为分离开,把所有与一个特定的状态相关的行为都放入一个对象中,使得应用程序在控制的时候,只需要关心状态的切换,而不用关心这个状态对应的真正处理。

  • 更好的扩展性

引入了状态处理的公共接口后,使得扩展新的状态变得非常容易,只需要新增加一个实现状态处理的公共接口的实现类,然后在进行状态维护的地方,设置状态变化到这个新的状态即可。

  • 显示化进行状态转换

状态模式为不同的状态引入独立的对象,使得状态的转换变得更加明确。而且状态对象可以保证上下文不会发生内部状态不一致的情况,因为上下文中只有一个变量来记录状态对象,只要为这一个变量赋值就可以了。

缺点

  • 一个状态对应一个状态处理类,会使得程序引入太多的状态类,这样程序变得杂乱。

思考

本质

根据状态来分离和选择行为。

何时选用

  • 如果一个对象的行为取决与它的状态,而且它必须在运行时刻根据状态来改变它的行为,可以使用状态模式,来把状态和行为分离开。虽然分离开了,但状态和行为是有对应关系的,可以在运行期间,通过改变状态,就能调用到该状态对应的状态处理对象上去,从而改变对象的行为。
  • 如果一个操作中含有庞大的多分支语句,而且这些分支依赖与该对象的状态,可以使用状态模式,把各个分支的处理分散包装到单独的对象处理类中,这样,这些分支对应的对象就可以不依赖于其他对象而独立变化了。

相关模式

  • 状态模式和策略模式

这是两个结构相同,功能各异的模式。具体的在策略模式里面已经讲过了,这里就不再赘述。

  • 状态模式和观察者模式

这两个模式乍一看,功能是相似的,但是又有区别,可以组合使用。
这两个模式都是在状态发生改变的时候触发行为,只不过观察者模式的行为是固定的,那就是通知所有的观察者;而状态模式是根据状态来选择不同的处理。
从表面上来看,两个模式功能相似,观察者模式的被观察者就好比状态模式中的上下文,观察者中被观察者状态发生改变的时候,触发通知所有观察者的方法就好比状态模式中,根据状态的变化选择对应的状态处理。
但是实际上这两个模式是不同的,观察者模式的目的是在被观察者的状态发生变化的时候,触发观察者联动,具体如何处理观察者模式不管;而状态模式的主要目的是,根据状态来分离和选择行为,当状态改变的时候,动态地改变行为。
这两个模式是可以组合使用的,比如在观察者模式的观察者部分,当被观察者对象的状态发生了改变,触发通知了所有观察者以后,观察者该怎么处理呢?这个时候就可以使用状态模式,根据通知过来的状态选择相应的处理。

  • 状态模式和单例模式

这两个模式可以组合使用,可以把状态模式中的状态处理类实现成单例。

  • 状态模式和享元模式

这两个模式可以组合使用
由于状态模式把状态对应的行为分散到多个状态对象中,会造成很多细粒度的状态对象,可以把这些状态处理对象通过享元模式来共享,从而节省资源。