状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。状态模式是一种对象行为型模式。大家着重理解对象,多种状态
状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。 状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。
在状态模式结构图中包含如下几个角色:
环境类实际上是真正拥有状态的对象,我们只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。在状态模式结构图中,环境类Context与抽象状态类State之间存在单向关联关系,在Context中定义了一个State对象。在实际使用时,它们之间可能存在更为复杂的关系,State与Context之间可能也存在依赖或者关联关系。
在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换,通常有两种实现状态转换的方式:
(1) 统一由环境类来负责状态之间的转换。此时,环境类还充当了状态管理器(State Manager)角色,在环境类的业务方法中通过对某些属性值的判断实现状态转换,还可以提供一个专门的方法用于实现属性判断和状态转换。
(2)由具体状态类来负责状态之间的转换。可以在具体状态类的业务方法中判断环境类的某些属性值再根据情况为环境类设置新的状态对象,实现状态转换,同样,也可以提供一个专门的方法来负责属性值的判断和状态转换。此时,状态类与环境类之间就将存在依赖或关联关系,因为状态类需要访问环境类中的属性值。
以日常电梯为例,将电梯简单分为四种状态:开门、关门、运行、停止
。
如果不使用状态模式,普通if…else if …逻辑如何实现呢?
/**
* 普通实现结构
*/
public void liftState(String state) {
if (Constant.OPEN.equals(state)) {
//open
//close
} else if (Constant.CLOSE.equals(state)) {
//open
//close
//running
//stop
} else if (Constant.RUNNING.equals(state)) {
//running
//stop
} else if (Constant.STOP.equals(state)) {
//open
//close
//running
//stop
}
}
以上模式的实现,很明显不遵循设计模式的“开闭原则”,后期维护存在一定的问题。 下面使用状态模式如何改进。 电梯状态每个状态还都要有特定的行为,比如在开门的状态下,电梯只能关门,而不能运行;在关门状态下,电梯可以运行、开门等。用一张表来表示这个关系:
状态/动作 | 开门 | 关门 | 运行 | 停止 |
---|---|---|---|---|
开门状态 | O | O | X | X |
关门状态 | O | O | O | |
运行状态 | X | X | O | |
停止状态 | O | X | O |
定义抽象状态类:分别定义电梯的四种状态方法。
public abstract class LiftState {
protected LiftContext liftContext;
public void setLiftContext(LiftContext liftContext){
this.liftContext = liftContext;
}
abstract void openState();
abstract void closeState();
abstract void runningState();
abstract void stopState();
}
这里解释一下为什么 LiftState 类里面会有一个LiftContext 对象。它的作用是去调节状态的变化,它就是电梯,电梯状态肯定是针对电梯来说的,所以选择组合LiftContext 对象。 下面看下具体状态实现类
public class LiftOpen extends LiftState {
@Override
public void openState() {
System.out.println("电梯开门...");
}
@Override
public void closeState() {
//修改状态
super.liftContext.changeState(Constant.CLOSE);
//动作委托为CloseState来执行,也就是委托给了LifeClose子类执行这个动作
super.liftContext.close();
}
@Override
public void runningState() {
}
@Override
public void stopState() {
//电梯开门,已经处于停止状态,无需执行其他操作
}
}
public class LiftClose extends LiftState {
@Override
public void openState() {
super.liftContext.changeState(Constant.OPEN);
super.liftContext.open();
}
@Override
public void closeState() {
System.out.println("电梯关门...");
}
@Override
public void runningState() {
super.liftContext.changeState(Constant.RUNNING);
super.liftContext.running();
}
@Override
public void stopState() {
super.liftContext.changeState(Constant.STOP);
super.liftContext.stop();
}
}
public class LiftRunning extends LiftState {
@Override
public void openState() {
}
@Override
public void closeState() {
//电梯运行过程中,门始终处于关闭状态
}
@Override
public void runningState() {
System.out.println("电梯运行中...");
}
@Override
public void stopState() {
super.liftContext.changeState(Constant.STOP);
super.liftContext.stop();
}
}
public class LiftStop extends LiftState {
@Override
public void openState() {
super.liftContext.changeState(Constant.OPEN);
super.liftContext.open();
}
@Override
public void closeState() {
}
@Override
public void runningState() {
super.liftContext.changeState(Constant.RUNNING);
super.liftContext.running();
}
@Override
public void stopState() {
System.out.println("电梯停止...");
}
}
环境角色Context中,定义当前电梯的状态,处于初始化状态。同时提供外部访问方法。
public class LiftContext {
//定义当前电梯状态
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
//通知各个实现类中
this.liftState.setLiftContext(this);
}
/**
* 环境类定义状态转化方法
* @param state
*/
public void changeState(String state) {
switch (state) {
case Constant.OPEN:
this.setLiftState(new LiftOpen());
break;
case Constant.CLOSE:
this.setLiftState(new LiftClose());
break;
case Constant.RUNNING:
this.setLiftState(new LiftRunning());
break;
case Constant.STOP:
this.setLiftState(new LiftStop());
break;
default:
break;
}
}
public void open() {
this.liftState.openState();
}
public void close() {
this.liftState.closeState();
}
public void running() {
this.liftState.runningState();
}
public void stop() {
this.liftState.stopState();
}
}
public class LiftClient {
public static void main(String[] args) {
LiftContext liftContext = new LiftContext();
liftContext.changeState(Constant.OPEN);
liftContext.open();
liftContext.close();
liftContext.running();
liftContext.stop();
}
}
运行结果:
源代码地址:https://github.com/stream-source/stream-source/tree/master/informal-essay
总结,对于状态模式,自己理解起来挺费劲的,总感觉有一个梗过不去,实际上可以将状态模式与责任链模式对比起来理解(后期分享责任链),就很清晰了。状态模式将对象内部状态的转化,对于外部访问并不知道。 状态模式:主要理解状态和行为。状态是如何产生的,以及这个状态怎么过渡到其他状态(执行动作)。
参考资料: 1.状态模式笔记 2.《Java 设计模式》