定义

对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

结构

主要角色:

  1. 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
  2. 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
  3. 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

结构图:
**状态模式(State Pattern) - 图1

应用

马里奥吃蘑菇

小马里奥(Small Mario)、超级马里奥(Super Mario)、火焰马里奥(Fire Mario)、斗篷马里奥(Cape Mario
状态模式(State Pattern) - 图2

  1. public interface IMario {
  2. State getName();
  3. void obtainMushRoom(MarioStateMachine stateMachine);
  4. void obtainCape(MarioStateMachine stateMachine);
  5. void obtainFireFlower(MarioStateMachine stateMachine);
  6. void meetMonster(MarioStateMachine stateMachine);
  7. }
  8. public class SmallMario implements IMario {
  9. private static final SmallMario instance = new SmallMario();
  10. private SmallMario() {}
  11. public static SmallMario getInstance() {
  12. return instance;
  13. }
  14. @Override
  15. public State getName() {
  16. return State.SMALL;
  17. }
  18. @Override
  19. public void obtainMushRoom(MarioStateMachine stateMachine) {
  20. stateMachine.setCurrentState(SuperMario.getInstance());
  21. stateMachine.setScore(stateMachine.getScore() + 100);
  22. }
  23. @Override
  24. public void obtainCape(MarioStateMachine stateMachine) {
  25. stateMachine.setCurrentState(CapeMario.getInstance());
  26. stateMachine.setScore(stateMachine.getScore() + 200);
  27. }
  28. @Override
  29. public void obtainFireFlower(MarioStateMachine stateMachine) {
  30. stateMachine.setCurrentState(FireMario.getInstance());
  31. stateMachine.setScore(stateMachine.getScore() + 300);
  32. }
  33. @Override
  34. public void meetMonster(MarioStateMachine stateMachine) {
  35. // do nothing...
  36. }
  37. }
  38. // 省略SuperMario、CapeMario、FireMario类...
  39. public class MarioStateMachine {
  40. private int score;
  41. private IMario currentState;
  42. public MarioStateMachine() {
  43. this.score = 0;
  44. this.currentState = SmallMario.getInstance();
  45. }
  46. public void obtainMushRoom() {
  47. this.currentState.obtainMushRoom(this);
  48. }
  49. public void obtainCape() {
  50. this.currentState.obtainCape(this);
  51. }
  52. public void obtainFireFlower() {
  53. this.currentState.obtainFireFlower(this);
  54. }
  55. public void meetMonster() {
  56. this.currentState.meetMonster(this);
  57. }
  58. public int getScore() {
  59. return this.score;
  60. }
  61. public State getCurrentState() {
  62. return this.currentState.getName();
  63. }
  64. public void setScore(int score) {
  65. this.score = score;
  66. }
  67. public void setCurrentState(IMario currentState) {
  68. this.currentState = currentState;
  69. }
  70. }

利用单例

  1. public interface IMario {
  2. State getName();
  3. void obtainMushRoom(MarioStateMachine stateMachine);
  4. void obtainCape(MarioStateMachine stateMachine);
  5. void obtainFireFlower(MarioStateMachine stateMachine);
  6. void meetMonster(MarioStateMachine stateMachine);
  7. }
  8. public class SmallMario implements IMario {
  9. private static final SmallMario instance = new SmallMario();
  10. private SmallMario() {}
  11. public static SmallMario getInstance() {
  12. return instance;
  13. }
  14. ...
  15. }

优点

  1. 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  2. 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  3. 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

    缺点

  4. 状态模式的使用必然会增加系统的类与对象的个数。

  5. 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
  6. 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。

扩展

有限状态机

有限状态机,英文翻译是 Finite State Machine,缩写为 FSM,简称为状态机。状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。
实现方式有三种:

  • 基于条件判断的最简单的实现方法
  • 基于查表法的实现方法
  • 基于状态模式的实现方法

由于基于状态模式的实现方法会增加类个数,增加系统复杂度,故在查表法能应付的情况下,最好使用查表法。