定义

状态模式允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类

  • 这个模式将状态封装成独立的类, 并将动作委托到当前代表的对象
  • 当状态流转是固定的时候, 就适合放在context中; 当转换是更动态的时候, 通常放在状态类中

案例

背景
image.png
原来设计代码: 现在要增加一个状态, 这个类就需要大面积的更改, 不易拓展

  1. /**
  2. * Machine
  3. *
  4. * @author xinzhang
  5. * @author Shenzhen Greatonce Co Ltd
  6. * @version 2020/1/22
  7. * 投入硬币-->转动曲柄-->分发糖果
  8. */
  9. public class GumballMachine {
  10. // 糖果机有四种状态, 有无硬币, 有硬币, 无糖果, 有糖果
  11. private final int NO_COIN = 0;
  12. private final int HAS_COIN = 1;
  13. private final int NO_GUMBALL = 2;
  14. private final int HAS_GUMBALL = 3;
  15. private int state = NO_GUMBALL;
  16. private int gumballNum;
  17. public GumballMachine(int gumballNum) {
  18. this.gumballNum = gumballNum;
  19. if (gumballNum > 0) {
  20. state = NO_COIN;
  21. }
  22. }
  23. /**
  24. * 投入硬币
  25. */
  26. public void insertCoin() {
  27. switch (state) {
  28. case NO_COIN:
  29. case HAS_GUMBALL:
  30. System.out.println("有硬币了!请转动曲柄!");
  31. state = HAS_COIN;
  32. break;
  33. case HAS_COIN:
  34. System.out.println("有硬币了别再投了!");
  35. break;
  36. case NO_GUMBALL:
  37. System.out.println("投也没用, 糖果售罄!");
  38. break;
  39. }
  40. }
  41. /**
  42. * 退回硬币
  43. */
  44. public void ejectCoin() {
  45. switch (state) {
  46. case NO_COIN:
  47. System.out.println("请先投入硬币!");
  48. break;
  49. case NO_GUMBALL:
  50. System.out.println("糖没了, 硬币也是不会退的!");
  51. break;
  52. case HAS_GUMBALL:
  53. System.out.println("已经转动了曲柄, 不给退!");
  54. break;
  55. case HAS_COIN:
  56. System.out.println("退回硬币!");
  57. state = NO_COIN;
  58. break;
  59. }
  60. }
  61. /**
  62. * 转动曲柄
  63. */
  64. public void turnCrack() {
  65. switch (state) {
  66. case NO_COIN:
  67. System.out.println("先投硬币才能转!");
  68. break;
  69. case HAS_COIN:
  70. if (gumballNum > 0) {
  71. state = HAS_GUMBALL;
  72. } else {
  73. state = NO_GUMBALL;
  74. }
  75. break;
  76. case HAS_GUMBALL:
  77. System.out.println("转过一次不能再转了!");
  78. break;
  79. case NO_GUMBALL:
  80. System.out.println("没糖, 转也没用!");
  81. break;
  82. }
  83. }
  84. /**
  85. * 发放糖果
  86. */
  87. public void dispense() {
  88. switch (state) {
  89. case NO_COIN:
  90. System.out.println("先付钱!");
  91. break;
  92. case HAS_COIN:
  93. System.out.println("先转曲柄!");
  94. break;
  95. case NO_GUMBALL:
  96. System.out.println("没有糖果了!");
  97. break;
  98. case HAS_GUMBALL:
  99. gumballNum--;
  100. if (gumballNum <= 0) {
  101. System.out.println("现在没糖了!");
  102. state = NO_GUMBALL;
  103. } else {
  104. System.out.println("发一个糖果!");
  105. state = NO_COIN;
  106. }
  107. }
  108. }
  109. }

使用状态模式后:
image.png

Machine, 持有四种状态类(注意多态写法),构造方法中初始化, 作为外部调用的入口, 去调用状态的动作

  1. public class Machine {
  2. private State noCoinState;
  3. private State hasCoinState;
  4. private State noGumballState;
  5. private State hasGumballState;
  6. private State state = noGumballState;
  7. private int count = 0;
  8. public Machine(int count) {
  9. noCoinState = new NoCoinState(this);
  10. hasCoinState = new HasCoinState(this);
  11. noGumballState = new NoGumballState(this);
  12. hasGumballState = new HasGumballState(this);
  13. this.count = count;
  14. if (count > 0) {
  15. state = noCoinState;
  16. }
  17. }
  18. public void insertCoin() {
  19. state.insertCoin();
  20. }
  21. public void ejectCoin() {
  22. state.ejectCoin();
  23. }
  24. public void turnCrack() {
  25. state.turnCrack();
  26. }
  27. public void dispense() {
  28. state.dispense();
  29. }
  30. public void releaseGumball() {
  31. System.out.println("释放一个糖果!");
  32. if (count > 0) {
  33. count--;
  34. }
  35. }
  36. public State getNoCoinState() {
  37. return noCoinState;
  38. }
  39. public State getHasCoinState() {
  40. return hasCoinState;
  41. }
  42. public State getNoGumballState() {
  43. return noGumballState;
  44. }
  45. public State getHasGumballState() {
  46. return hasGumballState;
  47. }
  48. public void setHasCoinState(State hasCoinState) {
  49. this.hasCoinState = hasCoinState;
  50. }
  51. public void setNoGumballState(State noGumballState) {
  52. this.noGumballState = noGumballState;
  53. }
  54. public void setHasGumballState(State hasGumballState) {
  55. this.hasGumballState = hasGumballState;
  56. }
  57. public void setNoCoinState(State noCoinState) {
  58. this.noCoinState = noCoinState;
  59. }
  60. public State getState() {
  61. return state;
  62. }
  63. public void setState(State state) {
  64. this.state = state;
  65. }
  66. public int getCount() {
  67. return count;
  68. }
  69. public void setCount(int count) {
  70. this.count = count;
  71. }
  72. }

State接口

  1. public interface State {
  2. void insertCoin();
  3. void ejectCoin();
  4. void turnCrack();
  5. void dispense();
  6. }

NoCoinState, 持有machine对象, 在投币动作中, 改变machine的状态

  1. public class NoCoinState implements State {
  2. Machine machine;
  3. public NoCoinState(Machine machine) {
  4. this.machine = machine;
  5. }
  6. @Override
  7. public void insertCoin() {
  8. System.out.println("有硬币了, 请转动曲柄!");
  9. machine.setState(machine.getHasCoinState());
  10. }
  11. @Override
  12. public void ejectCoin() {
  13. System.out.println("没有硬币, 无法退!");
  14. }
  15. @Override
  16. public void turnCrack() {
  17. System.out.println("没有硬币, 不能转曲柄!");
  18. }
  19. @Override
  20. public void dispense() {
  21. System.out.println("请投币!");
  22. }
  23. }

HasCoinState, 在转动曲柄动作中改变machine的状态

  1. public class HasCoinState implements State {
  2. Machine machine;
  3. public HasCoinState(Machine machine) {
  4. this.machine = machine;
  5. }
  6. @Override
  7. public void insertCoin() {
  8. System.out.println("已经有硬币了, 别投了!");
  9. }
  10. @Override
  11. public void ejectCoin() {
  12. System.out.println("退出硬币!");
  13. machine.setState(machine.getNoCoinState());
  14. }
  15. @Override
  16. public void turnCrack() {
  17. System.out.println("转动曲柄!");
  18. machine.setState(machine.getHasGumballState());
  19. }
  20. @Override
  21. public void dispense() {
  22. System.out.println("请转曲柄!");
  23. }
  24. }

HasGumballState, 在发糖动作中调用machine的方法, 并获取糖果数量来改变machine的状态

  1. public class HasGumballState implements State {
  2. Machine machine;
  3. public HasGumballState(Machine machine) {
  4. this.machine = machine;
  5. }
  6. @Override
  7. public void insertCoin() {
  8. System.out.println("再投没用!");
  9. }
  10. @Override
  11. public void ejectCoin() {
  12. System.out.println("已转曲柄!不能退!");
  13. }
  14. @Override
  15. public void turnCrack() {
  16. System.out.println("已转了一次!请稍等!");
  17. }
  18. @Override
  19. public void dispense() {
  20. machine.releaseGumball();
  21. if (machine.getCount() > 0) {
  22. System.out.println("发糖!");
  23. machine.setState(machine.getNoCoinState());
  24. } else {
  25. System.out.println("没糖了!");
  26. machine.setState(machine.getNoGumballState());
  27. }
  28. }
  29. }

NoGumballState

  1. public class NoGumballState implements State {
  2. Machine machine;
  3. public NoGumballState(Machine machine) {
  4. this.machine = machine;
  5. }
  6. @Override
  7. public void insertCoin() {
  8. System.out.println("没有糖果了, 投币也没用!");
  9. }
  10. @Override
  11. public void ejectCoin() {
  12. System.out.println("无法退币!");
  13. }
  14. @Override
  15. public void turnCrack() {
  16. System.out.println("没有糖果了, 转也没有用!");
  17. }
  18. @Override
  19. public void dispense() {
  20. System.out.println("发放失败!没有糖果了!");
  21. }
  22. }

这样设计的好处:
1.让每个状态的行为局部化到他自己的类中;
2.对修改关闭, 对拓展开放;
3.更易阅读和理解
image.png