1. 介绍
- 状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。状态模式与策略模式的结构几乎完全一样,但他们的目的和本质却是完全不一样的。
2. 定义
- 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
3. UML类图

- Context:环境类,定义客户端需要的接口,通过维护一个State实现类的实例(这个实例定义了对象的当前状态),负责具体状态的切换。
- State:抽象状态类或接口,通过定义一个或一组接口方法,表示相应状态下的行为
- ConcreteStateA/ConcreteStateB/...:具体状态实现类,通过实现State接口或抽象类,并实现抽象方法,来表示A/B...状态下的相对应的行为
4. 使用场景
- 代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有大量的多分支语句(if-else/switch-case),且这些分支依赖于该对象的状态。(状态模式将每一个条件分支放入一个独立的类中,每一种状态均可作为一个单独的对象,而且各对象间互不耦合,便可通过多态来去除重复的分支语句)
- 对象的行为取决于它的状态,并且行为会随着状态的改变而改变。
5. 简单实现
- 需求:电视在开机状态时能调音量和频道以及关机但不能开机,而当关机状态时则只能开机
5.1 使用普通方式
public class TvController {
private static final int POWER_OFF = 0;
private static final int POWER_ON = 1;
private int mState = POWER_OFF;
public void powerOn() {
if (mState == POWER_ON) {
System.out.println("遥控器闪两下红灯提示电视已经开机");
} else {
mState = POWER_ON;
System.out.println("电视开机");
}
}
public void powerOff() {
if (mState == POWER_ON) {
mState = POWER_OFF;
System.out.println("电视关机");
} else {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
}
public void nextChannel() {
if (mState == POWER_ON) {
System.out.println("下一频道");
} else {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
}
public void prevChannel() {
if (mState == POWER_ON) {
System.out.println("上一频道");
} else {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
}
public void turnUp() {
if (mState == POWER_ON) {
System.out.println("调高音量");
} else {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
}
public void turnDown() {
if (mState == POWER_ON) {
System.out.println("调低音量");
} else {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
}
}
- 可见普通方式方法内含有重复的if-else代码,而且这还只有两种状态,假如新增状态,每个方法都得因为新增的状态而改动代码,难以维护
5.2 使用状态模式
public interface TvState {
void powerOn();
void powerOff();
void nextChannel();
void prevChannel();
void turnUp();
void turnDown();
}
public class PowerOnState implements TvState {
@Override
public void powerOn() {
System.out.println("遥控器闪两下红灯提示电视已经开机");
}
@Override
public void powerOff() {
System.out.println("电视关机");
}
@Override
public void nextChannel() {
System.out.println("下一频道");
}
@Override
public void prevChannel() {
System.out.println("上一频道");
}
@Override
public void turnUp() {
System.out.println("调高音量");
}
@Override
public void turnDown() {
System.out.println("调低音量");
}
}
public class PowerOffState implements TvState{
@Override
public void powerOn() {
System.out.println("电视开机");
}
@Override
public void powerOff() {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
@Override
public void nextChannel() {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
@Override
public void prevChannel() {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
@Override
public void turnUp() {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
@Override
public void turnDown() {
System.out.println("遥控器闪两下红灯提示电视并未开机");
}
}
- TvController可以理解为UML类图中的Context
public class TvController {
private TvState tvState;
public void setTvState(TvState tvState) {
this.tvState = tvState;
}
public void powerOn() {
tvState.powerOn();
setTvState(new PowerOnState());
}
public void powerOff() {
tvState.powerOff();
setTvState(new PowerOffState());
}
public void nextChannel() {
tvState.nextChannel();
}
public void prevChannel() {
tvState.prevChannel();
}
public void turnUp() {
tvState.turnUp();
}
public void turnDown() {
tvState.turnDown();
}
}
public class client {
public static void main(String[] args) {
TvController tvController = new TvController();
tvController.setTvState(new PowerOnState());
tvController.powerOff();
tvController.powerOn();
tvController.nextChannel();
tvController.prevChannel();
tvController.turnUp();
tvController.turnDown();
}
}
运行结果:
电视关机
电视开机
下一频道
上一频道
调高音量
调低音量
- 从前后两种方式可以发现,虽然应用状态模式使得类和对象增加了,但可维护性大大增强,各状态下的行为均封装到不同的类中,互不影响,大大降低了耦合度,使得可维护性大大增强,特别是在后期新增更多状态的时候
6. 源码中的使用场景
- Android系统中的Wifi状态的管理(开启状态时扫描附近wifi并展示,关闭时列表为空,还有开启中...、关闭中等状态的处理)
- 自己项目中登录与未登录状态下各种逻辑的封装处理
7. 优缺点
- 优点:
- 避免了过多的条件语句,使得结构更清晰,提高代码的可维护性。
- 每个状态都是一个子类,方便增加和修改状态。
- 状态被放置到类的内部,外部调用不需要知道类的内部如何实现状态和行为的变换。
- 缺点: