状态模式
夏天送清凉助手----电风扇
马上就进入夏天了,电风扇真的是居家旅行必备之消暑利器。记得小时候,在外面玩回家满头大汗,一打开电风扇,那个爽快。大家都用过风扇,有研究过它的工作模式么?今天我们用程序来模拟一下它是怎么工作的。我们操作它的动作有:打开电源,设置想要的风速,风扇运行,关闭电源。在写代码之前,我们要先想想风扇运行都有哪些状态?关闭状态时,干什么都不行只能打开电源,运行时,可以调风速,可以关闭,但肯定不能再打开了。我们画个表格
写代码前,理清思路,画时序图,类图;可以让我们的代码更健壮,更完美;走一步看一步的编码方式,是不推荐的,低效,易走弯路
public class AutoFan { public final static int OPEN = 1; public final static int LOW_SPEED = 2; public final static int HIGH_SPEED = 3; public final static int CLOSE = 4; private int state=4; /** * 打开风扇 */ public void open(){ if(this.state == CLOSE){ System.out.println("打开电源"); state = OPEN; }else { System.out.println("电源已打开"); } } /** * 设成低速 */ public void setLowSpeed(){ if(this.state == OPEN || this.state == HIGH_SPEED){ System.out.println("设成低速"); state = LOW_SPEED; }else if(this.state == LOW_SPEED){ System.out.println("已经是低速"); }else if(this.state == CLOSE){ System.out.println("电源关闭不可设置"); } } /** * 设成高速 */ public void setHighSpeed(){ if(this.state == OPEN || this.state == LOW_SPEED){ System.out.println("设成高速"); state = HIGH_SPEED; }else if(this.state == HIGH_SPEED){ System.out.println("已经是高速"); }else if(this.state == CLOSE){ System.out.println("电源关闭不可设置"); } } /** * 关闭风扇 */ public void close(){ if(this.state != CLOSE){ System.out.println("关闭电源"); state = CLOSE; }else { System.out.println("电源已关闭"); } } }
public static void main(String[] args){ AutoFan autoFan = new AutoFan(); autoFan.open(); autoFan.setHighSpeed(); autoFan.setLowSpeed(); autoFan.open(); autoFan.close(); } 运行结果 打开电源 设成高速 设成低速 电源已打开 关闭电源
这样写完之后,我们的电风扇也可以很好的运转了。但是这似乎违反了我们的开闭原则,就是对扩展开放,对修改关闭。当我们需要对风扇加新的功能时,比如说加入中速档、加摇头功能时,我们就要对源码进行大改。代码量少的时候还好,当代码量多了起来之后,每有新的需求过来,我们就要改动源码,加入if else if;这简直就是永无止境的if else 地狱。编程体验很差,还很容易出bug,让之前可用的功能不可用。针对开闭原则,将可能变化的抽象出来,我们将风扇的状态抽象成一个接口,接口中有风扇的所有方法,然后让具体的状态去实现对应的方法。
认识状态模式
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
画一下类图
接下来根据新的设计模式,我们来重构一下上面的代码
风扇状态接口
public interface State {
/**
* 打开风扇
*/
public void open();
/**
* 设成低速
*/
public void setLowSpeed();
/**
* 设成高速
*/
public void setHighSpeed();
/**
* 关闭风扇
*/
public void close();
}
开启状态
public class OpenState implements State {
private Context context;
public OpenState(Context context){
this.context=context;
}
public void open() {
System.out.println("电源已经打开了");
}
public void setLowSpeed() {
System.out.println("设置成低速");
context.setState(new LowSpeedState(this.context));
}
public void setHighSpeed() {
System.out.println("设置成高速");
context.setState(new HighSpeedState(this.context));
}
public void close() {
System.out.println("电源关闭");
context.setState(new CloseState(this.context));
}
}
低速状态
public class LowSpeedState implements State {
private Context context;
public LowSpeedState(Context context){
this.context=context;
}
public void open() {
System.out.println("电源已经打开了");
}
public void setLowSpeed() {
System.out.println("已经设置成低速了");
}
public void setHighSpeed() {
System.out.println("设置成高速");
context.setState(new HighSpeedState(this.context));
}
public void close() {
System.out.println("电源关闭");
context.setState(new CloseState(this.context));
}
}
高速状态
public class HighSpeedState implements State {
private Context context;
public HighSpeedState(Context context){
this.context=context;
}
public void open() {
System.out.println("电源已经打开了");
}
public void setLowSpeed() {
System.out.println("设置成低速");
context.setState(new LowSpeedState(this.context));
}
public void setHighSpeed() {
System.out.println("已经设置成高速了");
}
public void close() {
System.out.println("电源关闭");
context.setState(new CloseState(this.context));
}
}
关闭状态
public class CloseState implements State {
private Context context;
public CloseState(Context context){
this.context=context;
}
public void open() {
System.out.println("电源打开");
context.setState(new OpenState(this.context));
}
public void setLowSpeed() {
System.out.println("不可设置成低速");
}
public void setHighSpeed() {
System.out.println("不可设置成高速");
}
public void close() {
System.out.println("电源已经关闭了");
}
}
上下文类
public class Context {
private State state;
public void setState(State state) {
this.state = state;
}
/**
* 打开风扇
*/
public void open(){
state.open();
}
/**
* 设成低速
*/
public void setLowSpeed(){
state.setLowSpeed();
}
/**
* 设成高速
*/
public void setHighSpeed(){
state.setHighSpeed();
}
/**
* 关闭风扇
*/
public void close(){
state.close();
}
}
测试
public static void main(String[] args){
Context context = new Context();
context.setState(new CloseState(context));
context.open();
context.setLowSpeed();
context.setHighSpeed();
context.setHighSpeed();
context.open();
context.close();
}
运行结果
电源打开
设置成低速
设置成高速
已经设置成高速了
电源已经打开了
电源关闭
状态模式的核心是封装,状态的变更引起行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。State和ConcreteState比较好理解,Context是环境角色,定义客户端需要的接口,并且负责具体状态的切换。
状态模式的应用
优点:
- 结构清晰:避免了过多的switch...case或者if...else语句,避免了程序的复杂性,提高了系统的可维护性
- 遵循设计原则:很好的体现了开闭原则和单一职责原则,正因为遵循了设计原则,所以才有了第一个优点的可维护性。新增状态和修改状态都很方便
- 封装性非常好:状态变换放置到类的内部来实现,外部调用不用知道类内部如何实现状态和行为的变换
缺点:
子类太多,会造成类膨胀。相对于缺点,优点更加的突出
使用场景:
- 行为随状态改变而改变的场景
- 条件、分支判断语句的替代者
注意事项:
状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受到状态约束的情况下可以使用状态模式,
且使用时对象的状态最好不要超过5个。