状态模式
夏天送清凉助手----电风扇
马上就进入夏天了,电风扇真的是居家旅行必备之消暑利器。记得小时候,在外面玩回家满头大汗,一打开电风扇,那个爽快。大家都用过风扇,有研究过它的工作模式么?今天我们用程序来模拟一下它是怎么工作的。我们操作它的动作有:打开电源,设置想要的风速,风扇运行,关闭电源。在写代码之前,我们要先想想风扇运行都有哪些状态?关闭状态时,干什么都不行只能打开电源,运行时,可以调风速,可以关闭,但肯定不能再打开了。我们画个表格
写代码前,理清思路,画时序图,类图;可以让我们的代码更健壮,更完美;走一步看一步的编码方式,是不推荐的,低效,易走弯路
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个。