问题
电脑城开始不满足于仅仅销售数码产品,着手研发产品。前期的话,先研发一些比较简单的产品,分别是台灯和电视。台灯的设计:在关闭状态下,点击开关按钮,从弱光—>强光—>关闭。电视的遥控器设计,在关闭状态下,点击开关按钮,打开电视,点击其他按钮,显示红灯,提示未开机。在开机状态下,点击开关按钮,关机,点击其他按钮,执行对应操作。如果不做任何考虑的话,可以在点击按钮时,判断当前状态,根据状态的不同来决定执行对应的操作。我们先来看看状态模式如何解决上述问题,再来看看其优劣。
实现
先看台灯的实现
public class TableLamp {
private LampState lampState;
public void onClick() {
this.lampState.click(this);
}
public TableLamp() {
lampState = new LampOffState();
}
public void setLampState(LampState lampState) {
this.lampState = lampState;
}
}
台灯的状态定义
public abstract class LampState {
/**
* 台灯开关点击事件
*/
public abstract void click(TableLamp tableLamp);
}
public class LampOffState extends LampState {
@Override
public void click(TableLamp tableLamp) {
System.out.println("from off to weak");
tableLamp.setLampState(new LampWeakState());
}
}
public class LampStrongState extends LampState {
@Override
public void click(TableLamp tableLamp) {
System.out.println("from strong to off");
tableLamp.setLampState(new LampOffState());
}
}
public class LampWeakState extends LampState {
@Override
public void click(TableLamp tableLamp) {
System.out.println("from weak to strong");
tableLamp.setLampState(new LampStrongState());
}
}
再来看电视遥控器
public class TvController {
private TvState tvState;
public TvController() {
tvState = new OffState();
}
public void setTvState(TvState tvState) {
this.tvState = tvState;
}
public void nextChannel() {
this.tvState.nextChannel();
}
public void preChannel() {
this.tvState.preChannel();
}
public void turnUp() {
this.tvState.turnUp();
}
public void turnDown() {
this.tvState.turnDown();
}
}
电视的状态定义
public abstract class TvState {
public abstract void nextChannel();
public abstract void preChannel();
public abstract void turnUp();
public abstract void turnDown();
}
public class OffState extends TvState {
public OffState() {
System.out.println("关机");
}
@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 OnState extends TvState{
public OnState() {
System.out.println("开机");
}
@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("开机状态,调低音量");
}
}
执行代码
TableLamp tableLamp = new TableLamp();
tableLamp.onClick();
tableLamp.onClick();
tableLamp.onClick();
TvController controller = new TvController();
controller.nextChannel();
controller.preChannel();
controller.turnDown();
controller.turnUp();
//开机
controller.setTvState(new OnState());
controller.nextChannel();
controller.preChannel();
controller.turnDown();
controller.turnUp();
执行结果
from off to weak
from weak to strong
from strong to off
关机
关机状态,红灯闪烁提示
关机状态,红灯闪烁提示
关机状态,红灯闪烁提示
关机状态,红灯闪烁提示
开机
开机状态,下一频道
开机状态,上一频道
开机状态,调低音量
开机状态,调大音量
简介
可以看出:台灯和遥控器的实现方式有些微的不同。我将其按照是否会切换状态进行了分类,台灯开关点击就是直接对状态的切换,而遥控器点击事件是不对状态直接切换,而是在不同状态下其他按钮不同的操作(当然遥控器也是可以通过开关按钮进行状态切换的,只是我为了区分,将其分开)。
状态切换更类似于有限状态机,通过不同的操作,发生状态的变化,可以清晰的开出状态的切换过程,从A状态由何种操作转移到B状态。
此外,状态切换下,不同按钮下的操作,若按照传统实现方式,在点击时,switch当前状态,在不同的case下执行不同的操作,而这显然不符合开闭原则,若增加状态的话,所有的原始代码都需要进行修改。现在的话,只需要增加一个状态类即可,并且可以清晰的看出当前状态下,所有按钮点击后执行的操作。
总结
状态模式的定义是在一个对象的状态变化时改变其行为,看上去像是改变了其自身所属的类一样。其适用场景:对于一个对象有较多的状态切换,并且在不同状态下对于该对象的众多操作也不一样时。优点:符合单一职责,开闭原则;消除了大量状态判断语句;缺点:在少量状态和操作的情况下,会显得过度设计。