介绍
状态模式,又称状态对象模式,状态模式是对象的行为模式。状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。
模式角色
状态模式有以下角色:
- Context(环境),定义了客户端需要的接口并维护一个State的实例,将与状态相关的操作委托给当前的ConcreteState对象来处理。
- State(抽象状态),定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
- ConcreteState(具体状态),实现抽象状态定义的接口。
模式结构图
(结构图来源于网上)
模式实现
关于模式实现,如果根据“谁来定义状态的变化”来区分,可以分为两种:
- Context定义状态的变化
- State子类自行决定下一个状态
模式实现-Context定义状态的变化
/**
* 现有一个系统,有两种状态,开机和关机状态。开机时执行开机操作,关机时执行关机操作。
*/
class StateContext {
public static final int STATE_TURN_ON = 0; // 开机状态
public static final int STATE_TURN_OFF = 1; // 关机状态
private State state;
private int status; // STATE_TURN_ON、STATE_TURN_OFF
public StateContext() {
// 默认关机
state = new TurnOffState();
status = STATE_TURN_OFF;
}
public int getStatus() {
return status;
}
/**
* 状态过渡
* @param status 状态值,see STATE_TURN_ON、STATE_TURN_OFF。
* 关于state对象创建问题
* 1:动态的创建所偶需要的状态对象(这里采用的动态创建)
* 2:事先创建所有的对象。
*/
public void setStatus(int status) {
this.status = status;
if (status == STATE_TURN_ON) {
state = new TurnOnState();
} else if (status == STATE_TURN_OFF) {
state = new TurnOffState();
}
}
/**
* 客户端感兴趣的业务方法(根据实际业务需求编写)
*/
public void turnOperation() {
state.action();
}
}
/**
* 抽象状态
*/
public abstract class State {
public abstract void action();
}
/**
* 具体状态类--开机状态
*/
class TurnOnState extends State {
@Override
public void action() {
// 业务逻辑
}
}
/**
* 具体状态类--关机状态
*/
class TurnOffState extends State {
@Override
public void action() {
// 业务逻辑
}
}
class StateClient {
public static void main(String[] args) {
StateContext context = new StateContext();
context.turnOperation();
// 开机
context.setStatus(StateContext.STATE_TURN_ON);
context.turnOperation();
// 关机
context.setStatus(StateContext.STATE_TURN_OFF);
context.turnOperation();
}
}
注意在StateContext 关于state对象创建问题,可以动态的创建所偶需要的状态对象,也可以事先创建所有的对象。采取何种方式根据当前环境决定,如性能、程序状态变化快速…
模式实现-State子类自行决定下一个状态
/**
* 现有一个系统,有两种状态,开机和关机状态。开机时执行开机操作,关机时执行关机操作。
*/
class StateContext {
private State state;
public StateContext() {
// 默认关机
state = new TurnOffState();
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
/**
* 客户端感兴趣的业务方法(根据实际业务需求编写)
*/
public void turnOperation() {
state.action(this);
}
}
/**
* 抽象状态
*/
public abstract class State {
/**
* 将context传入,后续可以用context做很多操作
* @param context
*/
public abstract void action(StateContext context);
}
/**
* 具体状态类--开机状态
*/
class TurnOnState extends State {
@Override
public void action(StateContext context) {
// 业务逻辑省略...
// 决定下一个状态(这里决定为关机状态)
context.setState(new TurnOffState());
}
}
/**
* 具体状态类--关机状态
*/
class TurnOffState extends State {
@Override
public void action(StateContext context) {
// 业务逻辑省略...
// 决定下一个状态(这里决定为关机状态)
context.setState(new TurnOnState());
}
}
class StateClient {
public static void main(String[] args) {
// 初始化后默认关机
StateContext context = new StateContext();
// 关机操作,操作完成后Context持有的状态设置开机状态
context.turnOperation();
// 开机操作,操作完成后Context持有的状态设置关机状态
context.turnOperation();
// 关机操作,操作完成后Context持有的状态设置开机状态
context.turnOperation();
//.......
}
}
模式使用场景
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
模式扩展
在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享,其结构图如下图。
(结构图来源于网上)
模式混淆
策略模式经常与状态模式混淆,这个时候考察环境角色是否有明显的状态和状态的过渡。如果环境角色只有一个状态,那么就应当使用策略模式。策略模式的特点是:一旦环境角色选择了一个具体策略类,那么在整个环境类的生命周期里它都不会改变这个具体策略类。而状态模式则适用于另一种情况,即环境角色有明显的状态转移。在环境类的生命周期里面,会有几个不同的状态对象被使用。