随遇而安–状态模式
1. 状态模式介绍
状态模式中的行为是由状态来决定的,不同状态下有不同的行为,状态模式的行为是平行的,不可替换的;状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。
2.状态模式的定义
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变其类。
3.状态模式的使用场景
(1). 一个对象的行为取决于它的状态,并且
它必须在运行时根据状态改变它的行为
(2). 代码中含有大量有对象状态相关的条件语句,例如,一个操作中含有庞大的多分支语句(if-else或switch-case),且这些分支依赖于该对象的状态。
状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的状态来去除过多的,重复的if-else等分支语句。
4.状态模式的角色划分和UML类图
(1). UML类图
(2). 角色划分
- Context:环境类,定义客户感兴趣的接口,维护一个State子类的实例,这个实例定义了对象的当前状态。
- State:抽象状态类或者状态接口,定义一个或者一组接口,表示该状态下的行为。
- ConcreateStateA、ConcreateStateB:具体状态类,每一个具体的状态类实现抽象State中定义的接口,从而达到不同状态下的不同行为。
5.案列分析
下面我们就以电视机遥控器为例来演示下状态模式的实现。
需求分析:电视机有开机运行和关机停止运行两种状态,在开机状态下可以通过遥控器进行频道切换、调整音量等操作,但是此时重复开机按键无效;而在关机状态下,频道切换、调整音量、关机都操作都是无效的,只有按开机按钮才会生效,电视机的内部状态决定了遥控器的行为。
(1). 普通代码实现
/**
* Created by zhangqing on 2018/3/4.
* 电视机遥控器,含有开机,关机,下一频道、上一频道、调高音量、调低音量这个几个功能
*/
public class TvController {
//开机状态
private final static int POWER_ON = 1;
//关机状态
private final static int POWER_OFF = 2;
private int mState = POWER_OFF;
public void powerOn() {
if (mState == POWER_OFF) {
System.out.println("开机啦");
}
mState = POWER_ON;
}
public void powerOff() {
if (mState == POWER_ON) {
System.out.println("关机啦");
}
mState = POWER_OFF;
}
public void nextChannel() {
if (mState == POWER_ON) {
System.out.println("下一频道");
} else {
System.out.println("两个红灯提示,没有开机");
}
}
public void preChannel() {
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("两个红灯提示,没有开机");
}
}
}
可以看到,在TvController类中,通过mState字段存储了电视机的状态,并且在各个操作中根据状态来判断是否应该执行,导致了每个功能中都有if-else,代码冗余、相对较为混乱,如果后期开发过程中状态变成5个,函数变成10个呢,会变得越来越难以维护,状态模式就是为解决这类问题而出现的,下面我们就来看下,将状态用对象来代替,将行为封装到类中使得在不同状态下有不同的实现,讲if-else从tvController中去掉,整个结构变得清晰起来,代码如下:
(2). 使用状态模式来实现代码
TvState.java
/**
* Created by zhangqing on 2018/3/4.
* 电视机状态接口,定义了电视操作的函数
*/
public interface TvState {
public void nextChannel();
public void prevChannel();
public void turnUp();
public void turnDown();
}
PowerOffState.java
/**
* Created by zhangqing on 2018/3/4.
* 关机状态,此时只有开机功能是有效的
*/
public class PowerOffState implements TvState {
@Override
public void nextChannel() {
}
@Override
public void prevChannel() {
}
@Override
public void turnUp() {
}
@Override
public void turnDown() {
}
}
PowerOnState.java
/**
* Created by zhangqing on 2018/3/4.
* 开机状态,此时再触发开机功能不做任何操作
*/
public class PowerOnState implements TvState {
@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("调低音量");
}
}
PowerController.java
/**
* Created by zhangqing on 2018/3/4.
* 电源操作接口
*/
public interface PowerController {
public void powerOn();
public void powerOff();
}
TvController.java
/**
* Created by zhangqing on 2018/3/4.
* 电视遥控器,类似于经典状态模式中的Context
*/
public class TvController implements PowerController {
TvState mTvState;
public void setTvState(TvState tvState) {
mTvState = tvState;
}
@Override
public void powerOn() {
setTvState(new PowerOnState());
System.out.println("开机啦");
}
@Override
public void powerOff() {
setTvState(new PowerOffState());
System.out.println("关机啦");
}
public void nextChannel() {
mTvState.nextChannel();
}
public void preChannel() {
mTvState.prevChannel();
}
public void turnUp() {
mTvState.turnUp();
}
public void turnDown() {
mTvState.turnDown();
}
}
上述视线中,抽象了一个TvState接口,该接口中有操作电视的所有函数,该接口有开机状态(PowerOnState)和关机状态(PowerOffState)两个实现类,开机状态下只有开机功能是无效的,关机状态下只有开机功能是可用的,其它的功能都不会生效,电视机的内部状态影响了电视遥控器的行为。
总结:
状态模式将这不同状态下不同的行为封装到状态类中,在进行操作时,将这些功能转发给对应的状态下对象,不同的状态有不同的实现,通过多态的形式去掉了重复、杂乱的f-else语句。