状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。

什么是有限状态机?

有限状态机,英文翻译是 Finite State Machine,缩写为 FSM,简称为状态机。状态机有 3 个组成部分:

  • 状态(State)
  • 事件(Event)也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。
  • 动作(Action)

形象的例子:

“超级马里奥”游戏不知道你玩过没有?在游戏中,马里奥可以变身为多种形态,比如小马里奥(Small Mario)、超级马里奥(Super Mario)、火焰马里奥(Fire Mario)、斗篷马里奥(Cape Mario)等等。在不同的游戏情节下,各个形态会互相转化,并相应的增减积分。比如,初始形态是小马里奥,吃了蘑菇之后就会变成超级马里奥,并且增加 100 积分。

其中,马里奥的不同形态就是状态机中的“状态”,游戏情节(比如吃了蘑菇)就是状态机中的“事件”,加减积分就是状态机中的“动作”。

image.png

  1. public enum State {
  2. SMALL(0),
  3. SUPER(1),
  4. FIRE(2),
  5. CAPE(3);
  6. private int value;
  7. private State(int value) {
  8. this.value = value;
  9. }
  10. public int getValue() {
  11. return this.value;
  12. }
  13. }
  14. public class MarioStateMachine {
  15. private int score;
  16. private State currentState;
  17. public MarioStateMachine() {
  18. this.score = 0;
  19. this.currentState = State.SMALL;
  20. }
  21. // 各种事件
  22. public void obtainMushRoom() {
  23. //TODO
  24. }
  25. public void obtainCape() {
  26. //TODO
  27. }
  28. public void obtainFireFlower() {
  29. //TODO
  30. }
  31. public void meetMonster() {
  32. //TODO
  33. }
  34. public int getScore() {
  35. return this.score;
  36. }
  37. public State getCurrentState() {
  38. return this.currentState;
  39. }
  40. }
  41. public class ApplicationDemo {
  42. public static void main(String[] args) {
  43. MarioStateMachine mario = new MarioStateMachine();
  44. mario.obtainMushRoom(); // 触发事件
  45. int score = mario.getScore();
  46. State state = mario.getCurrentState();
  47. System.out.println("mario score: " + score + "; state: " + state);
  48. }
  49. }

状态机实现方式一:分支逻辑法

  1. 判断当前状态 (产生分支)
  2. 切换状态
  1. public class MarioStateMachine {
  2. private int score;
  3. private State currentState;
  4. public MarioStateMachine() {
  5. this.score = 0;
  6. this.currentState = State.SMALL;
  7. }
  8. public void obtainMushRoom() {
  9. if (currentState.equals(State.SMALL)) {
  10. this.currentState = State.SUPER;
  11. this.score += 100;
  12. }
  13. }
  14. public void obtainCape() {
  15. if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER) ) {
  16. this.currentState = State.CAPE;
  17. this.score += 200;
  18. }
  19. }
  20. public void obtainFireFlower() {
  21. if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER) ) {
  22. this.currentState = State.FIRE;
  23. this.score += 300;
  24. }
  25. }
  26. public void meetMonster() {
  27. if (currentState.equals(State.SUPER)) {
  28. this.currentState = State.SMALL;
  29. this.score -= 100;
  30. return;
  31. }
  32. if (currentState.equals(State.CAPE)) {
  33. this.currentState = State.SMALL;
  34. this.score -= 200;
  35. return;
  36. }
  37. if (currentState.equals(State.FIRE)) {
  38. this.currentState = State.SMALL;
  39. this.score -= 300;
  40. return;
  41. }
  42. }
  43. public int getScore() {
  44. return this.score;
  45. }
  46. public State getCurrentState() {
  47. return this.currentState;
  48. }
  49. }

状态机实现方式二:查表法

image.png

  1. public enum Event {
  2. GOT_MUSHROOM(0),
  3. GOT_CAPE(1),
  4. GOT_FIRE(2),
  5. MET_MONSTER(3);
  6. private int value;
  7. private Event(int value) {
  8. this.value = value;
  9. }
  10. public int getValue() {
  11. return this.value;
  12. }
  13. }
  14. public class MarioStateMachine {
  15. private int score;
  16. private State currentState;
  17. private static final State[][] transitionTable = {
  18. {SUPER, CAPE, FIRE, SMALL},
  19. {SUPER, CAPE, FIRE, SMALL},
  20. {CAPE, CAPE, CAPE, SMALL},
  21. {FIRE, FIRE, FIRE, SMALL}
  22. };
  23. private static final int[][] actionTable = {
  24. {+100, +200, +300, +0},
  25. {+0, +200, +300, -100},
  26. {+0, +0, +0, -200},
  27. {+0, +0, +0, -300}
  28. };
  29. public MarioStateMachine() {
  30. this.score = 0;
  31. this.currentState = State.SMALL;
  32. }
  33. public void obtainMushRoom() {
  34. executeEvent(Event.GOT_MUSHROOM);
  35. }
  36. public void obtainCape() {
  37. executeEvent(Event.GOT_CAPE);
  38. }
  39. public void obtainFireFlower() {
  40. executeEvent(Event.GOT_FIRE);
  41. }
  42. public void meetMonster() {
  43. executeEvent(Event.MET_MONSTER);
  44. }
  45. private void executeEvent(Event event) {
  46. int stateValue = currentState.getValue();
  47. int eventValue = event.getValue();
  48. this.currentState = transitionTable[stateValue][eventValue];
  49. this.score = actionTable[stateValue][eventValue]; // +=?
  50. }
  51. public int getScore() {
  52. return this.score;
  53. }
  54. public State getCurrentState() {
  55. return this.currentState;
  56. }
  57. }

状态机实现方式三:状态模式

MarioStateMachine 和各个状态类之间是双向依赖关系。MarioStateMachine 依赖各个状态类是理所当然的,但是,反过来,各个状态类为什么要依赖 MarioStateMachine 呢?这是因为,各个状态类需要更新 MarioStateMachine 中的两个变量,score 和 currentState。

  1. public interface IMario { //所有状态类的接口
  2. State getName();
  3. //以下是定义的事件
  4. void obtainMushRoom();
  5. void obtainCape();
  6. void obtainFireFlower();
  7. void meetMonster();
  8. }
  9. public class SmallMario implements IMario {
  10. private MarioStateMachine stateMachine;
  11. public SmallMario(MarioStateMachine stateMachine) {
  12. this.stateMachine = stateMachine;
  13. }
  14. @Override
  15. public State getName() {
  16. return State.SMALL;
  17. }
  18. @Override
  19. public void obtainMushRoom() {
  20. stateMachine.setCurrentState(new SuperMario(stateMachine));
  21. stateMachine.setScore(stateMachine.getScore() + 100);
  22. }
  23. @Override
  24. public void obtainCape() {
  25. stateMachine.setCurrentState(new CapeMario(stateMachine));
  26. stateMachine.setScore(stateMachine.getScore() + 200);
  27. }
  28. @Override
  29. public void obtainFireFlower() {
  30. stateMachine.setCurrentState(new FireMario(stateMachine));
  31. stateMachine.setScore(stateMachine.getScore() + 300);
  32. }
  33. @Override
  34. public void meetMonster() {
  35. // do nothing...
  36. }
  37. }
  38. public class SuperMario implements IMario {
  39. private MarioStateMachine stateMachine;
  40. public SuperMario(MarioStateMachine stateMachine) {
  41. this.stateMachine = stateMachine;
  42. }
  43. @Override
  44. public State getName() {
  45. return State.SUPER;
  46. }
  47. @Override
  48. public void obtainMushRoom() {
  49. // do nothing...
  50. }
  51. @Override
  52. public void obtainCape() {
  53. stateMachine.setCurrentState(new CapeMario(stateMachine));
  54. stateMachine.setScore(stateMachine.getScore() + 200);
  55. }
  56. @Override
  57. public void obtainFireFlower() {
  58. stateMachine.setCurrentState(new FireMario(stateMachine));
  59. stateMachine.setScore(stateMachine.getScore() + 300);
  60. }
  61. @Override
  62. public void meetMonster() {
  63. stateMachine.setCurrentState(new SmallMario(stateMachine));
  64. stateMachine.setScore(stateMachine.getScore() - 100);
  65. }
  66. }
  67. // 省略CapeMario、FireMario类...
  68. public class MarioStateMachine {
  69. private int score;
  70. private IMario currentState; // 不再使用枚举来表示状态
  71. public MarioStateMachine() {
  72. this.score = 0;
  73. this.currentState = new SmallMario(this);
  74. }
  75. public void obtainMushRoom() {
  76. this.currentState.obtainMushRoom();
  77. }
  78. public void obtainCape() {
  79. this.currentState.obtainCape();
  80. }
  81. public void obtainFireFlower() {
  82. this.currentState.obtainFireFlower();
  83. }
  84. public void meetMonster() {
  85. this.currentState.meetMonster();
  86. }
  87. public int getScore() {
  88. return this.score;
  89. }
  90. public State getCurrentState() {
  91. return this.currentState.getName();
  92. }
  93. public void setScore(int score) {
  94. this.score = score;
  95. }
  96. public void setCurrentState(IMario currentState) {
  97. this.currentState = currentState;
  98. }
  99. }