1.1概述
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。这就是状态模式的定义。
一个对象的状态依赖于它的变量的取值情况,对象在不同的运行环境中,可能具有不同的状态。在许多情况下,对象调用方法所产生的行为效果依赖于它当时的状态。
例如,一个温度计(Thermometer)类的实例:温度计类通过调用方法showMessage()显示有关信息时,需要根据当前自己温度(temperature)变量的值来显示有关信息,即根据自己的状态来决定showMessage()方法所体现的具体行为,这就要求showMessage()方法中有许多条件分支语句,例如:
public void showMessage(){ if(temperature <= -20){ System.out.println("现在是低温"+temperature); } if(temperature >= 30){ System.out.println("现在是高温"+temperature); } }
我们注意到showMessage()方法的行为依赖于temperature的大小,这就使Thermometer类的实例在应对需求变化时缺乏弹性,不能很好地满足用户的需求。例如,有些用户可能需要温度计当其temperature的值大于60显示某些重要的信息;有些用户当其温度temperature的值在18到25之间显示重要的信息。显然为达到上述要求,必须修改上面的代码才可以满足用户需求,者显然不是人们喜欢做的。
现在我们需要重新考虑温度计(Thermometer)类的设计,发现温度计类中因用户需求变化而需要修改的代码都和对象的状态有关,即与温度的大小有关,因此,按照面向抽象、不面向实现的设计原则,应当将对象的状态从当前对象中分离出去,即将一个对象的状态封装在另外一个类中。现在,设计一个抽象类:TemperatureState类,该类规定了显示和温度有关的信息的方法showTemperature()。具体类关系图如下图一所示:
图一:封装对象的状态
Thermometer类包含ThermometerState类声明的若干个变量,表面Thermometer类的实例:温度计类可以将任何温度计状态类的子类的实例作为自己的状态,而且温度计类的实例可以把和状态有关的请求委派给所维护状态的对象。
状态模式的关键是将对象的状态封装成为独立的类,对象调用方法时,可以委托当前对象所具有的状态调用相应的方法,是当前对象看起来好像修改了它的类。
1.2模式的结构
状态模式包括以下三种角色:
(1)环境(Context):环境是一个类,该类含有抽象状态(State)声明的变量,可以引用任何具体状态类的实例。用户对该环境(Context)类的实例在某种状态下的行为感兴趣。
(2)抽象状态(State):抽象状态是一个接口或抽象类。抽象状态中定义了与环境(Context)的一个特定状态相关的若干个方法。
(3)具体状态(Concrete State):具体状态是实现(扩展)抽象状态(抽象类)的实例。
状态模式结构的类图如下图二所示:
图二:状态模式的类图
1.3状态模式的优点
(1)使用一个类封装对象的一种状态,很容易增加新的状态。
(2)在状态模式中,环境(Context)中不必出现大量的条件判断语句。环境(Context)实例所呈现的状态变得更加清晰、容易理解。
(3)使用状态模式可以让用户程序很方便地切换环境实例的状态。
(4)使用状态模式不会让环境的实例中出现内部状态不一致的情况。
(5)当状态对象没有实例变量时,环境的各个实例可以共享一个状态对象。
1.4适合使用状态模式的情景
(1)一个对象的行为依赖于它的状态,并且它必须在运行时根据状态改变它的行为。
(2)需要编写大量的条件分支语句来决定一个操作的行为,而且这些条件恰好表示对象的一种状态。
1.5状态模式的使用
以下通过一个简单的问题来描述怎样使用状态模式,这个简单的问题就是:设计带文字提示信息的温度计。带文字提示信息的温度计可以根据用户的需求显示某些提示信息,比如用户希望温度在0度以下时,显示提示信息为“低温温度”,温度在10~26度之间时,显示提示信息为“正常温度”,当温度大于39度时,显示提示信息为“高温温度”。
首先看一下本实例构建框架具体类和1.2模式的结构中类图的对应关系,如下图所示:
(1)环境(Context)
本问题中,环境角色是Thermometer类,代码如下:
package com.liuzhen.seventeen_state; public class Thermometer { TemperatureState state; public void showMessage(){ System.out.println("************"); state.showTemperature(); System.out.println("************"); } public void setState(TemperatureState state){ this.state = state; } }
(2)抽象状态(State)
对于本问题,抽象状态(State)是TemperatureState接口,代码如下:
package com.liuzhen.seventeen_state; public interface TemperatureState { public void showTemperature(); }
(3)具体状态(Concrete State)
对于本问题,共有三个具体状态角色,分别是LowState,MiddleState,HeightState类,代码如下:
LowState.java
package com.liuzhen.seventeen_state; public class LowState implements TemperatureState{ double n; LowState(double n){ if(n <= 0) this.n = n; } public void showTemperature(){ System.out.println("现在温度是"+n+",属于低温"); } }
MiddleState.java
package com.liuzhen.seventeen_state; public class MiddleState implements TemperatureState{ double n; MiddleState(double n){ if(n > 0 && n < 26) this.n = n; } public void showTemperature(){ System.out.println("现在温度是"+n+",属于正常温度"); } }
HeightState.java
package com.liuzhen.seventeen_state; public class HeightState implements TemperatureState{ double n; HeightState(double n){ if(n > 39) this.n = n; } public void showTemperature(){ System.out.println("现在温度是"+n+",属于高温"); } }
(4)具体使用
通过SeventeenApplication类来具体实现上述相关类和接口,来实现状态模式的运用,其代码如下:
package com.liuzhen.seventeen_state; public class SeventeenApplication { public static void main(String[] args){ TemperatureState state = new LowState(-12); Thermometer thermometer = new Thermometer(); thermometer.setState(state); thermometer.showMessage(); state = (TemperatureState) new MiddleState(20); thermometer.setState(state); thermometer.showMessage(); state = (TemperatureState) new HeightState(50); thermometer.setState(state); thermometer.showMessage(); } }
运行结果:
************ 现在温度是-12.0,属于低温 ************ ************ 现在温度是20.0,属于正常温度 ************ ************ 现在温度是50.0,属于高温 ************
参考资料:
1.Java设计模式/耿祥义,张跃平著.——北京:清华大学出版社,2009.5