初探设计模式之【状态】
一、场景
这里从一个例子开始引入状态设计模式。
以交通信号灯为例,有红、黄、绿三种颜色的信号灯,绿灯显示一段时间后,短暂切换到黄灯后,再切换到红灯。红灯显示一段一段时候,再短暂切换到黄灯,再切换到绿灯。总之,红灯和绿灯之间不能直接切换,需要借助黄灯转换一下。
我们需要实现一个交通信号灯,分别切换到红、黄、绿灯的方法。
二、传统实现
这样一种场景,如果使用传统的写法,大致可能是如下的思路:
切换到红灯:如果当前为 “红” , 则报错,因为当前已经是红灯了。如果当前是绿灯,也报错,因为绿灯不能直接切换到红灯。最后只剩下黄灯,黄灯可以切换红灯,让黄灯亮2秒钟。
切换到黄灯:如果当前为 “黄”,则报错,因为当前已经是黄灯了。否则,可以切换到黄灯,红灯或绿灯亮 6 秒。
剩下的就不列举了。
上面例子的每一个切换方法中,都需要写一系列的判断逻辑,这里你也许会说好像也还好,但如果状态变多,规则变复杂写,这里的一系列 if else 可能就很难看了,而且所有的逻辑都集中在一个类中,一堆的逻辑判断,很让人抓狂。我们可以尝试用状态模式来实现。
三、应用状态模式
将切换状态的行为抽取出来,编写一个 State 接口:
public interface State {
//切换到红灯
void switchToRed(TrafficLight trafficLight);
//切换到黄灯
void switchToYellow(TrafficLight trafficLight);
//切换到绿灯
void switchToGreen(TrafficLight trafficLight);
}
然后我们分别将红黄绿灯实现:
//红灯
public class RedState implements State{
@Override
public void switchToRed(TrafficLight trafficLight) {
System.out.println("ERROR! 已经是红灯了");
}
@Override
public void switchToYellow(TrafficLight trafficLight) {
trafficLight.setState(new YellowState());
System.out.println("黄灯亮 2 秒");
ThreadUtil.sleep(2_000);
}
@Override
public void switchToGreen(TrafficLight trafficLight) {
System.out.println("ERROR! 红灯不能直接切换为绿灯!");
}
}
//黄灯
public class YellowState implements State {
@Override
public void switchToRed(TrafficLight trafficLight) {
trafficLight.setState(new RedState());
System.out.println("红灯亮 6 秒");
ThreadUtil.sleep(6_000);
}
@Override
public void switchToYellow(TrafficLight trafficLight) {
System.out.println("已经是黄灯");
}
@Override
public void switchToGreen(TrafficLight trafficLight) {
trafficLight.setState(new GreenState());
System.out.println("绿灯亮 6 秒");
ThreadUtil.sleep(6_000);
}
}
//绿灯
public class GreenState implements State {
@Override
public void switchToRed(TrafficLight trafficLight) {
System.out.println("ERROR! 红灯不能直接切换为绿灯!");
}
@Override
public void switchToYellow(TrafficLight trafficLight) {
trafficLight.setState(new YellowState());
System.out.println("黄灯亮 2 秒");
ThreadUtil.sleep(2_000);
}
@Override
public void switchToGreen(TrafficLight trafficLight) {
System.out.println("ERROR! 已经是绿灯了!");
}
}
TrafficLight
表示交通信号灯,它需要实现切换信号灯的功能:
public class TrafficLight {
private State state = new GreenState();
public void setState(State state) {
this.state = state;
}
public void switchToRed() {
state.switchToRed(this);
}
public void switchToYellow(){
state.switchToYellow(this);
}
public void switchToGreen() {
state.switchToGreen(this);
}
}
在交通信号灯类中,我们不再需要实现复杂的状态判断,转而由状态自己去切换,而每种状态又有各自自己的实现,即每种灯切换到别的灯应该怎么处理,相比原有的判断逻辑,这样一下子变得清爽了不少。
看下打印结果:
黄灯亮 2 秒
红灯亮 6 秒
黄灯亮 2 秒
绿灯亮 6 秒
黄灯亮 2 秒
红灯亮 6 秒Process finished with exit code 0
四、理解
状态模式的应用将系统状态从系统环境中彻底抽离出来,状态接口确立了高层统一规范,使状态响应机制分立、自治,以一种松耦合的方式实现了系统状态与行为的联动机制(每种状态自己去处理当前状态应该处理的逻辑)。
状态模式的类结构如下:
State(状态接口):定义通用的状态规范标准,其中处理请求方法handle()将系统环境Context作为参数传入。对应本章例程中的状态接口State。
ConcreteStateA、ConcreteStateB、ConcreteStateC(状态实现A、状态实现B、状态实现C):具体的状态实现类,根据系统环境用于表达系统环境Context的各个状态,它们都要符合状态接口的规范。对应本章例程中的红灯状态Red、绿灯状态Green以及黄灯状态Yellow。
Context(系统环境):系统的环境,持有状态接口的引用,以及更新状态方法setState(),对外暴露请求发起方法request(),对应本章例程中的交通灯类 TrafficLight
注:本文参考了 《秒懂设计模式》