状态模式【state】
-
什么是状态模式
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。一般很难去理解,稍后我会以代码加文字的形式给大家解释清楚。 -
什么场景会用到状态模式
因为这个模式里面我看了很多网上的文章都拿电梯来举例,我觉得也很合理,因为电梯每个不同的状态都可能有存在多种处理(状态模式不仅仅只会像工作流一样一级一级地往下走,还有可能跳级去处理),比如,电梯在停止的情况下,可以运行,也可以打开电梯门和关闭电梯门,这个就涉及到多个状态的判断,电梯的原型图如下:
如果现在要大家开始写程序大家会怎么开始呢?当然包括我第一次接触的时候脑海里面想的无非就是传入一个指令,然后在程序中判断这个指令对应哪一个状态,最开始的代码起草是不是这样的:
enum State {
CLOSE(1),
MOVE(2),
STOP(3),
OPEN(4);
private int liftState;
State(int liftState) {
this.liftState = liftState;
}
}
class Lift{
public void operate(State state) {
switch (state) {
case MOVE:
System.out.println("moving..");
break;
case OPEN:
System.out.println("opening..");
break;
case CLOSE:
System.out.println("closing..");
break;
case STOP:
System.out.println("stopping..");
break;
}
}
}
没错,这个代码确实可以解决电梯的运行,但是我们要沉下心来想想这个是不是满足开始我们的需求,在电梯运行状态时是千万不能开门的,但是我们现在的代码来看就是只要是输入的指令是开门,我们就调用开门的逻辑,这个显然是很危险的事情,ok,那我们现有的代码就需要进行修改,需要在open的逻辑里面再加一层if-else判断是否能开门,这样就形成了多层的if判断,这个在coding里面算很危险的事情,一是维护性很差,比较难以读懂;二是如果后面需求发生了变更,这个代码肯定要进行修改,这显然也违背了开闭原则,所以这个情况下状态模式能够很好的解决该问题;
接下来我们仔细的看一下状态模式的解释:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。说的通俗易懂一点,我们把内部状态看成它的属性,也就是属性发生了变化,类似电梯运行状态变成了停止状态,行为这个可以理解成对象内部的方法,也就是方法内部的逻辑发生了变化,现在大家脑海里面是不是想到了多态和继承去重写父类的方法,那么从程序角度上面来想,怎么让对象的属性去改变对象的方法呢? 没错,这个属性就图上的LiftState,代码如下:
首先提供一个抽象的状态类
public abstract class LiftState {
protected Context context;
public void setContext(Context context) {
this.context = context;
}
public void open() {
System.out.println("do nothing..");
}
public void close() {
System.out.println("do nothing..");
}
public void move() {
System.out.println("do nothing..");
}
public void stop() {
System.out.println("do nothing..");
}
}
接下来是开门,关门,移动和停止的具体实现
public class OpenningState extends LiftState {
@Override
public void open() {
System.out.println("lift opening");
}
@Override
public void close() {
super.context.setLiftState(Context.CLOSING_STATE);
super.context.getLiftState().close();
}
}
public class ClosingState extends LiftState {
@Override
public void close() {
System.out.println("lift closing...");
}
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.getLiftState().open();
}
@Override
public void move() {
super.context.setLiftState(Context.MOVING_STATE);
super.context.getLiftState().move();
}
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.getLiftState().stop();
}
}
public class MovingState extends LiftState {
@Override
public void move() {
System.out.println("lift moving...");
}
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.getLiftState().stop();
}
}
public class StoppingState extends LiftState {
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.getLiftState().open();
}
@Override
public void move() {
super.context.setLiftState(Context.MOVING_STATE);
super.context.getLiftState().move();
}
@Override
public void stop() {
System.out.println("lift stopping...");
}
}
此类是个封装对象,里面有电梯的所有操作
public class Context {
public final static LiftState OPENING_STATE = new OpenningState();
public final static LiftState CLOSING_STATE = new ClosingState();
public final static LiftState MOVING_STATE = new MovingState();
public final static LiftState STOPPING_STATE = new StoppingState();
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
this.liftState.setContext(this);
}
public void open(){
this.liftState.open();
}
public void close(){
this.liftState.close();
}
public void move(){
this.liftState.move();
}
public void stop(){
this.liftState.stop();
}
}
测试代码如下:
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(Context.CLOSING_STATE);
context.open();
context.close();
context.move();
context.open();// do nothing
context.open();// do nothing
context.open();// do nothing
context.stop();
}
我们不难发现,运用这个程序可以很好的去实现不同状态的不同处理,如果我运行中,电梯无论怎么按我都不会打开,并且如果后期如果要加一个电梯内紧急通知功能,我只用去相应的增加类和该功能的实现即可,不需要去改动现有的逻辑。
看了这段代码,是不是对状态模式有了一个很清晰的认识了
- 适用性
在下面的两种情况下均可使用 State模式:
• 一个对象的行为取决于它的状态 , 并且它必须在运行时刻根据状态改变它的行为。
• 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状
态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State
模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对
象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
相关参考:
<设计模式之禅>,<设计模式,可复用面向对象软件的基础>
源码在此