状态模式

82 阅读3分钟

本文主要内容

  • 状态模式简介
  • 状态模式实例
  • 总结

1、状态模式简介

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类

状态模式的一般使用场景:

  • 一个对象的行为取决于它的状态,并且必须在运行时根据它的状态改变它的行为
  • 代码中包含大量与状态有关的条件语句,如有很多if else

从第2条来看,状态模式与策略模式类似,但它和策略模式是不一样的,状态模式的所有状态,肯定是有切换的,不同状态不同的行为,遇到不同的环境状态会变化。它的内含偏向于,不同状态下不同的处理逻辑。而策略模式,偏向于同样的功能不同的实现。

状态模式UML图

2、状态模式实例

我们以电视遥控器为例。遥控器可以调台,并且调节音量。但电视只有在开机的状态下才能实现上述功能,关机的时候按钮无效。那么我们应该怎么去抽象这个状态类呢?

使用状态模式时,非常重要的一点是分清楚哪里是不同状态下能做的操作?哪些是切换状态的因素。遥控器这个例子中,开关机显示是切换状态的因素,调台和调音量显示是不同状态下的操作。

当我们在定义状态接口时,一定不能把导致状态切换的因素也写到状态接口中,否则会很别扭。这么一分析,状态接口可以这么写:

public interface TvState {

public void nextChannel();
public void preChannel();
public void turnUp();
public void turnDown();
}

开机状态实现:

public class PowerOnState implements TvState{

@Override
public void nextChannel() {
	System.out.println("下一个频道");
}

@Override
public void preChannel() {
	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 nextChannel() {
}

@Override
public void preChannel() {
}

@Override
public void turnUp() {
}

@Override
public void turnDown() {
}
}

定义一个开关机的接口:

public interface PowerController {

public void powerOn();
public void powerOff();
}

遥控器:

public class TvController implements PowerController{

TvState mState;

public void setState(TvState tvState){
	mState = tvState;
}

@Override
public void powerOn() {
	System.out.println("开机了");
	setState(new PowerOnState());
}

@Override
public void powerOff() {
	System.out.println("关机了");
	setState(new PowerOffState());
}

public void nextChannel() {
	mState.nextChannel();
}

public void preChannel() {
	mState.preChannel();
}

public void turnUp() {
	mState.turnUp();
}

public void turnDown() {
	mState.turnDown();
}

public static void main(String[] args) {
	TvController controller = new TvController();
	controller.powerOn();
	controller.nextChannel();
	controller.turnDown();
	controller.powerOff();
	controller.turnUp();
}
}

3、总结

示例比较简单,最重要是理解状态模式。尤其是状态模式接口的定义,为啥不把开关机这个方法也写到状态接口当中来呢?因为如果开关机也在状态接口中,那怎么来切换状态呢?导致状态切换的原因一定要放在外面。另外就是认真体会下状态模式和策略模式之间的差异了。策略模式侧面的是不同算法,而状态模式侧重的是不同状态。