状态模式-State Pattern

状态模式: 在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

何时使用:代码中包含大量与对象状态有关的条件语句。

如何解决:将各种具体的状态类抽象出来。

类图如下:

这里主要包括三个类:

环境(Context)角色,也称上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。

  抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。

具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。

简单来说,对于某个事务,对于不同的状态会有不同的行为,我们可以用if-else条件、分支语句进行完成,但这样的扩展性,可维护性并不理想,每个新状态的产生,都要去修改原来的类,违背了"开闭原则"。在这里我们就可考虑用不同的状态行为类来进行,有新状态就新创建一个类,在context类中增加新状态代码。当状态行为需要改变时也很简单,找到相应的类修改即可,不必去修改context类,可维护性强。

当然这种方法也缺点

1、状态模式的使用必然会增加系统类和对象的个数。

2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱

3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

这里不好理解的是第三点,我们可以这样想,当有不同于之前所有的新状态时,如果新状态和旧状态有依赖或联系,如果不修改相关状态代码,可能就无法实现相应的功能。

特别注意:我们把状态转换规则定义在一个context类中,就使逻辑关系很清晰。Context是一个环境角色。作用是串联各个状态的过渡,不需要只要状态怎么转化的,这个工作由各个状态类完成。对外来说,我们只看到行为的改变,而不用知道是状态变化而引起的改变。

设计一个学生成绩的状态转换程序。分析:本实例包含了"不及格""中等"和"优秀" 3 种状态,当学生的分数小于 60 分时为"不及格"状态,当分数大于等于 60 分且小于 90 分时为"中等"状态,当分数大于等于 90 分时为"优秀"状态,我们用状态模式来实现这个程序。此处为了方便,采取Java实现:

  1. public class ScoreStateTest
  2. {
  3. public static void main(String[] args)
  4. {
  5. ScoreContext account=new ScoreContext();
  6. System.out.println("学生成绩状态测试:");
  7. account.add(30);
  8. account.add(40);
  9. account.add(25);
  10. account.add(-15);
  11. account.add(-25);
  12. }
  13. }
  14. //环境类
  15. class ScoreContext
  16. {
  17. private AbstractState state;
  18. ScoreContext()
  19. {
  20. state=new LowState(this);
  21. }
  22. public void setState(AbstractState state)
  23. {
  24. this.state=state;
  25. }
  26. public AbstractState getState()
  27. {
  28. return state;
  29. }
  30. public void add(int score)
  31. {
  32. state.addScore(score);
  33. }
  34. }
  35. //抽象状态类
  36. abstract class AbstractState
  37. {
  38. protected ScoreContext hj; //环境
  39. protected String stateName; //状态名
  40. protected int score; //分数
  41. public abstract void checkState(); //检查当前状态
  42. public void addScore(int x)
  43. {
  44. score+=x;
  45. System.out.print("加上:"+x+"分, 当前分数:"+score );
  46. checkState();
  47. System.out.println("分, 当前状态:"+hj.getState().stateName);
  48. }
  49. }
  50. //具体状态类:不及格
  51. class LowState extends AbstractState
  52. {
  53. public LowState(ScoreContext h)
  54. {
  55. hj=h;
  56. stateName="不及格";
  57. score=0;
  58. }
  59. public LowState(AbstractState state)
  60. {
  61. hj=state.hj;
  62. stateName="不及格";
  63. score=state.score;
  64. }
  65. public void checkState()
  66. {
  67. if(score>=90)
  68. {
  69. hj.setState(new HighState(this));
  70. }
  71. else if(score>=60)
  72. {
  73. hj.setState(new MiddleState(this));
  74. }
  75. }
  76. }
  77. //具体状态类:中等
  78. class MiddleState extends AbstractState
  79. {
  80. public MiddleState(AbstractState state)
  81. {
  82. hj=state.hj;
  83. stateName="中等";
  84. score=state.score;
  85. }
  86. public void checkState()
  87. {
  88. if(score<60)
  89. {
  90. hj.setState(new LowState(this));
  91. }
  92. else if(score>=90)
  93. {
  94. hj.setState(new HighState(this));
  95. }
  96. }
  97. }
  98. //具体状态类:优秀
  99. class HighState extends AbstractState
  100. {
  101. public HighState(AbstractState state)
  102. {
  103. hj=state.hj;
  104. stateName="优秀";
  105. score=state.score;
  106. }
  107. public void checkState()
  108. {
  109. if(score<60)
  110. {
  111. hj.setState(new LowState(this));
  112. }
  113. else if(score<90)
  114. {
  115. hj.setState(new MiddleState(this));
  116. }
  117. }
  118. }

    最后要提一下,状态模式和策略模式有着相同的类图, 但是它们的意图不同。策略模式更加侧重于行为或算法的替换,并且可以在运行时动态的任意替换,侧重点是使用不同算法族来解决问题。而状态模式更加侧重于状态的改变影响改变行为,而行为的执行结果又会导致状态发生变化,是一个状态和行为的变化过程。策略模式需要客户端必须完全知晓所有的策略方法,才能够知道究竟哪一个策略是当前需要的。而状态模式客户端在使用的时候,可以不必关心有哪些状态,他只需要去调用环境的行为就可以了,在环境的内部维护了这些状态。

原文地址:https://www.cnblogs.com/xiaxiaopi/p/12743901.html