在面向对象编程中,是用类表示对象的。在State模式中,用类来表示状态。State的意思就是“状态”。在现实生活中,会考虑各种东西的状态,但是几乎不会将状态当做“东西”看待。
用类来表示状态以后,就能通过切换类来方便地改变对象的状态。当需要增加新的状态时,如何修改代码也会很明确。

示例程序:

警戒状态每小时都会改变一次的警报系统。


- 有一个金库
- 金库与警报中心相连
- 金库里有警铃和正常通话用的电话
- 金库里有时钟,监视着现在的时间

- 白天的时间范畴是9:00-16.59,晚上的时间范畴是17:00-23:59和0:00-8.59

- 金库只能在白天使用
- 白天使用金库的话,会在警报中心留下记录
- 晚上使用金库的话,会向警报中心发送紧急事态通知

- 任何时候都可以使用警铃
- 使用警铃的话,会向警报中心发送紧急事态通知

- 任何时候都可以使用电话(但晚上只有留言电话)
- 白天使用电话的话,会呼叫警报中心
- 晚上用电话的话,会呼叫警报中心的留言电话

类和接口的一览表:

名字 说明
State 表示金库状态的接口
DayState 表示“白天”的类,实现了State接口
NightState 表示“晚上”的类,实现了State接口
Context 表示管理金库状态,并与警报中心联系的接口
SafeFrame 实现了Context接口。在它内部持有按钮和画面显示等UI信息
Main 测试程序行为的类

State接口:

State接口是表示金库状态的接口,在State接口中定义了以下事件对应的接口。

  • 设置时间
  • 使用金库
  • 按下警铃
  • 正常通话 ```java public interface State { public abstract void doClock(Context context, int hour); // 设置时间

    public abstract void doUse(Context context); // 使用金库

    public abstract void doAlarm(Context context); // 按下警铃

    public abstract void doPhone(Context context); // 正常通话 }

  1. <a name="R1SmR"></a>
  2. #### DayState类:
  3. DayState类表示白天的状态。<br />对于每个表示状态的类,都只会生成一个实例对象。因为如果每次发生状态改变时都生成一个实例的话,太浪费内存和时间了。为此,使用了单例模式。
  4. ```java
  5. public class DayState implements State {
  6. private static DayState singleton = new DayState();
  7. private DayState() {
  8. }
  9. public static State getInstance() {
  10. return singleton;
  11. }
  12. @Override
  13. public void doClock(Context context, int hour) {
  14. if (hour < 9 || 17 <= hour) {
  15. context.changeState(NightState.getInstance());
  16. }
  17. }
  18. @Override
  19. public void doUse(Context context) {
  20. context.recordLong("使用金库(白天)");
  21. }
  22. @Override
  23. public void doAlarm(Context context) {
  24. context.recordLong("按下警铃(白天)");
  25. }
  26. @Override
  27. public void doPhone(Context context) {
  28. context.callSecurity("正常通话(白天)");
  29. }
  30. public String toString() {
  31. return "[白天]";
  32. }
  33. }

NightState类:

  1. public class NightState implements State {
  2. private static NightState instance = new NightState();
  3. private NightState() {
  4. }
  5. public static State getInstance() {
  6. return instance;
  7. }
  8. @Override
  9. public void doClock(Context context, int hour) {
  10. if (9 <= hour && hour < 17) {
  11. context.changeState(DayState.getInstance());
  12. }
  13. }
  14. @Override
  15. public void doUse(Context context) {
  16. context.callSecurity("紧急:晚上使用金库!");
  17. }
  18. @Override
  19. public void doAlarm(Context context) {
  20. context.callSecurity("按下警铃(晚上)");
  21. }
  22. @Override
  23. public void doPhone(Context context) {
  24. context.recordLong("晚上的通话录音");
  25. }
  26. public String toString() {
  27. return "[晚上]";
  28. }
  29. }

Context接口:

负责管理状态和联系警报中心的接口。

  1. public interface Context {
  2. public abstract void setClock(int hour); // 设置时间
  3. public abstract void changeState(State state); // 改变状态
  4. public abstract void callSecurity(String msg); // 联系警报中心
  5. public abstract void recordLong(String msg); // 在警报中心留下记录
  6. }

SafeFrame:

safeFrame的构造函数做了以下处理:

  • 设置背景色;
  • 设置布局管理器;
  • 设置控件;
  • 设置监听器。

通过调用各个按钮的addActionListener方法来设置监听器。程序结构类似于观察者模式。

  1. public class SafeFrame extends Frame implements ActionListener, Context {
  2. private TextField textClock = new TextField(60);
  3. private TextArea textScreen = new TextArea(10, 60);
  4. private Button buttonUse = new Button("使用金库");
  5. private Button buttonAlarm = new Button("按下警铃");
  6. private Button buttonPhone = new Button("正常通话");
  7. private Button buttonExit = new Button("结束");
  8. private State state = DayState.getInstance();
  9. public SafeFrame(String title) {
  10. super(title);
  11. setBackground(Color.lightGray);
  12. setLayout(new BorderLayout());
  13. // 配置textClock
  14. add(textClock, BorderLayout.NORTH);
  15. textClock.setEditable(false);
  16. // 配置textScreen
  17. add(textScreen, BorderLayout.CENTER);
  18. textScreen.setEditable(false);
  19. // 为界面添加按钮
  20. Panel panel = new Panel();
  21. panel.add(buttonUse);
  22. panel.add(buttonAlarm);
  23. panel.add(buttonPhone);
  24. panel.add(buttonExit);
  25. // 配置界面
  26. add(panel, BorderLayout.SOUTH);
  27. // 显示
  28. pack();
  29. show();
  30. // 设置监听器
  31. buttonUse.addActionListener(this);
  32. buttonAlarm.addActionListener(this);
  33. buttonPhone.addActionListener(this);
  34. buttonExit.addActionListener(this);
  35. }
  36. @Override
  37. public void setClock(int hour) {
  38. String clockString = "现在时间是";
  39. if (hour < 10) {
  40. clockString += "0" + hour + ":00";
  41. } else {
  42. clockString += hour + ":00";
  43. }
  44. System.out.println(clockString);
  45. textClock.setText(clockString);
  46. state.doClock(this, hour);
  47. }
  48. @Override
  49. public void callSecurity(String msg) {
  50. textScreen.append("call!" + msg + "\n");
  51. }
  52. @Override
  53. public void recordLong(String msg) {
  54. textScreen.append("record ..." + msg + "\n");
  55. }
  56. // 修改状态
  57. @Override
  58. public void changeState(State state) {
  59. System.out.println("从" + this.state + "状态变为了" + state + "状态。");
  60. this.state = state;
  61. }
  62. // 按键被按下后该方法会被调用
  63. @Override
  64. public void actionPerformed(ActionEvent e) {
  65. System.out.println(e.toString());
  66. if (e.getSource() == buttonUse) {
  67. state.doUse(this);
  68. } else if (e.getSource() == buttonAlarm) {
  69. state.doAlarm(this);
  70. } else if (e.getSource() == buttonPhone) {
  71. state.doPhone(this);
  72. } else if (e.getSource() == buttonExit) {
  73. System.exit(0);
  74. } else {
  75. System.out.println("?");
  76. }
  77. }
  78. }

State模式中的登场角色:

State(状态)

State角色表示状态,定义了根据不同状态进行不同处理的接口。该接口是那些处理内容依赖于状态的方法的集合

ConcreteState(具体状态):

ConcreteState角色表示各个具体的状态,它实现了State接口。

Context(状态、前后关系、上下文):

Context角色持有表示当前状态的ConcreteState角色。此外,它还定义了供外部调用者使用State模式的接口。

拓展思路:

分而治之:

在编程时,这种思想非常适用于大规模的复杂处理。当遇到庞大而复杂的问题,不能用一般的方法解决时,可以先将问题分解为多个小问题。如果还是不能解决,就将其拆分开更小的问题,直至可以解决它们为止。
在State模式中,用类来表示状态,并为每一种具体的状态都定义一个相应的类。当状态非常多的时候,State模式的优势就会非常明显了。

相关的设计模式:

Singleton模式:

Singleton会经常出现在ConcreteState角色中。

Flyweight模式:

在表示状态的类中并没有定义任何实例字段。有时可以使用Flyweight模式在多个Context角色之间共享ConcreteState角色。