我们开发项目的过程中通常会碰到一种需求,某个对象包含多种状态变化的可能性,随着状态的变化行为也执行不同的动作。
以电梯来举例:
电梯最基本具备 开门、关门、上下走动、停止 这四种状态。状态与状态之间有约束的关系,比如只能在关门的情况下才能走动。
所以用传统面向过程的解决方案,就必须要用状态值来标记每一种状态,然后用多个if else语句来判断状态的执行顺序,每当增加一个新状态的时候就需要重新修改代码,这样就不符合开闭原则。
我们来看下如何用状态模式来优雅的处理问题。
public abstract class LiftState
{
protected Context context;
public void setContext(Context context)
{
this.context = context;
}
public abstract void open(); //电梯开门
public abstract void close(); //电梯关门
public abstract void stop(); //电梯停止
public abstract void run(); //电梯上下走动
}
//电梯处于开门状态
public class OpenState extends LiftState
{
@Override
public void open() {
//本身就处于开门状态
//do nothing
}
@Override
public void close() {
super.context.setLiftStatus(Context.closeState); //切换至关门状态
super.context.getLiftStatus().close(); //委托关门状态类 来执行具体的关门动作
}
@Override
public void stop() {
//电梯开门状态时,本身就停止的
//do nothing
}
@Override
public void run() {
//电梯开门状态时,不能走动
//do nothing
}
}
//电梯走动状态
public class RunState extends LiftState
{
@Override
public void open() {
//走动状态不能开门
//do nothing
}
@Override
public void close() {
//走动状态本身就是处于关门状态
//do nothing
}
@Override
public void stop() {
super.context.setLiftStatus(Context.stopState); //切换至停止状态
super.context.getLiftStatus().stop(); //委托停止状态类 来执行具体的停止动作
}
@Override
public void run() {
//本身就处于走动状态
//do nothing
}
}
//电梯关门状态类
public class CloseState extends LiftState
{
@Override
public void open() {
super.context.setLiftStatus(Context.openStatus); //切换至开门状态
super.context.getLiftStatus().open(); //委托开门状态类 来执行具体的开门动作
}
@Override
public void close() {
}
@Override
public void stop() {
}
@Override
public void run() {
super.context.setLiftStatus(Context.runState); //切换至走动状态
super.context.getLiftStatus().run(); //委托走动状态类 来执行具体的走动动作
}
}
//电梯停止状态类
public class StopState extends LiftState
{
@Override
public void open() {
super.context.setLiftStatus(Context.openStatus); //切换至开门状态
super.context.getLiftStatus().open(); //委托开门状态类 来执行具体的走动动作
}
@Override
public void close() {
}
@Override
public void stop() {
}
@Override
public void run() {
super.context.setLiftStatus(Context.runState); //切换至走动状态
super.context.getLiftStatus().run(); //委托走动状态类 来执行具体的走动动作
}
}
public class Context
{
public final static OpenState openStatus = new OpenState();
public final static CloseState closeState = new CloseState();
public final static RunState runState = new RunState();
public final static StopState stopState = new StopState();
private LiftState curStatus; //当前电梯状态
//设置电梯状态,并且把当前上下文的引用传递给具体的状态类,具体的状态类通过上下文的引用切换不同的状态。
public void setLiftStatus(LiftState liftStatus)
{
curStatus = liftStatus;
liftStatus.setContext(this);
}
//获取当前的电梯状态
public LiftState getLiftStatus()
{
return curStatus;
}
public void open()
{
curStatus.open();
}
public void close()
{
curStatus.close();
}
public void stop()
{
curStatus.stop();
}
public void run()
{
curStatus.run();
}
}
//客户端类
public class Client
{
public static void main()
{
Context context = new Context();
context.setLiftStatus(new CloseState()); //电梯初始化为关门状态
context.open();
context.close();
context.run();
context.stop();
}
}
需要说明的一点是:
Context 是一个上下文角色,它的作用就是串联各个状态的过渡,在LifeState抽象类中我们定义了并把这个上下文组合进来,病传递到了子类。也就是四个具体状态类根据上下文的环境来决定如何进行状态的过渡。
状态模式的优点:
首先,每个状态类都很简洁,同时也不需要用 if else 来判断状态的切换。
同时,如果我们要新增状态只需要创建新的状态类即可,是通过扩展而非修改的方式来实现功能,这是面向对象的一个重要原则,开闭原则。非常完美。