程序员人生 网站导航

设计模式 —— 状态模式(State Pattern)

栏目:php教程时间:2016-06-08 08:26:22

状态模式(State Pattern)

概念:状态模式 允许对象在内部状态改变时改变它的行动,对象看起来好像修改了它的类


在软件设计中,我们常常会遇需要编写有很多状态的程序。最简单的如乘坐电梯程序,当我们要坐电梯时需要判断电梯的状态,只有当电梯处于当前楼时我们才能乘坐,当电梯不在当前楼层时我们要按下按钮等待电梯到来。在平时1般都通过 if…elseswitch 判断状态后处理,这类固定的写法只有在软件后期不会更新时才可以(不过是不可能的),状态模式实际上是使用组合通过简单援用不同的状态 对象 来造成状态改变的假象。


组成:

状态模式

Context(上下文):1般是1个具有多个状态的类,当调用 request() 函数时会被拜托到状态对象。

State(状态接口):定义了所有具体状态的共同接口,任何状态都实现这个接口,这样就可以在状态之间相互转换。

ConcreteStateA(具体状态):实现状态接口,处理来自 Context 的要求。每一个 ConcreteState 都提供自己要求的实现。


例子:

我们实现电梯的例子,电梯的状态包括运行、停止、开门、关门。当电梯运行时我们等待电梯停止,停止时我们等待开门或运行,开门时进入后关门,进入后电梯变成关门状态。

状态模式

假定目前就这么多状态,用传统的 if…elseswitch 实现。

抽象状态类

public abstract class LiftState { //4种状态 final int OPEN_STATE = 1; final int STOP_STATE = 2; final int CLOSE_STATE = 3; final int RUN_STATE = 4; public abstract void setState(int state); //开门动作 public abstract void open(); //关门动作 public abstract void close(); //运行动作 public abstract void run(); //停止动作 public abstract void stop(); }

public class Lift extends LiftState{ private int state; public Lift(int state) { this.state = state; } @Override public void setState(int state) { this.state = state; } //开门动作 @Override public void open() { switch (state) { case OPEN_STATE: System.out.println("处于开门状态:甚么也不做"); break; case CLOSE_STATE: System.out.println("处于关门状态:甚么也不做"); break; case RUN_STATE: System.out.println("处于运行状态:甚么也不做"); break; case STOP_STATE: System.out.println("处于关门状态,开门..."); setState(OPEN_STATE); break; } } //关门动作 @Override public void close() { switch (state) { case OPEN_STATE: System.out.println("处于开门状态:关门..."); setState(CLOSE_STATE); break; case CLOSE_STATE: System.out.println("处于关门状态:甚么也不做"); break; case RUN_STATE: System.out.println("处于运行状态:甚么也不做"); break; case STOP_STATE: System.out.println("处于停止状态:甚么也不做"); break; } } //运行动作 @Override public void run() { switch (state) { case OPEN_STATE: System.out.println("处于开门状态:甚么也不做"); break; case CLOSE_STATE: System.out.println("处于关门状态:运行..."); setState(RUN_STATE); break; case RUN_STATE: System.out.println("处于运行状态:甚么也不做"); break; case STOP_STATE: System.out.println("处于停止状态:运行..."); setState(RUN_STATE); } } //停止动作 @Override public void stop() { switch (state) { case OPEN_STATE: System.out.println("处于开门状态:甚么也不做"); break; case CLOSE_STATE: System.out.println("处于关门状态:甚么也不做"); setState(CLOSE_STATE); break; case RUN_STATE: System.out.println("处于运行状态:停止..."); break; case STOP_STATE: System.out.println("处于停止状态:甚么也不做"); } } }

public class LiftRun { public static void main(String[] args) { //初始设置为停止状态 Lift lift = new Lift(2); //停止变成运行 lift.run(); //运行时不能开门 lift.open(); //运行变成停止 lift.stop(); //停止时不能开门 lift.close(); } }

状态模式

上面的实现没甚么问题,但大量的用了 switch…case,当我们想要为电梯增加1种状态时,会发现需要大量的改动代码,每个 switch 都要改,这也是《重构》中称 switch代码的坏味道。我们来通过 状态模式 改变它。


状态模式改进例子:

状态模式

LiftState:

public abstract class LiftState { Lift lift; public LiftState(Lift lift) { this.lift = lift; } //开门动作 public abstract void open(); //关门动作 public abstract void close(); //运行动作 public abstract void run(); //停止动作 public abstract void stop(); }

CloseState:

public class CloseState extends LiftState { public CloseState(Lift lift) { super(lift); } @Override public void open() { System.out.println("处于关闭状态...甚么也不做"); } @Override public void close() { System.out.println("处于关闭状态...甚么也不做"); } @Override public void run() { System.out.println("处于关闭状态:运行"); lift.setState(lift.getRunState()); } @Override public void stop() { System.out.println("处于关闭状态...甚么也不做"); } }

OpenState:

public class OpenState extends LiftState { public OpenState(Lift lift) { super(lift); } @Override public void open() { System.out.println("处于开门状态...甚么也不错"); } @Override public void close() { System.out.println("处于开门状态:关门"); lift.setState(lift.getCloseState()); } @Override public void run() { System.out.println("处于开门状态...甚么也不错"); } @Override public void stop() { System.out.println("处于开门状态...甚么也不错"); } }

RunState:

public class RunState extends LiftState { public RunState(Lift lift) { super(lift); } @Override public void open() { System.out.println("处于运行状态...甚么也不做"); } @Override public void close() { System.out.println("处于运行状态...甚么也不做"); } @Override public void run() { System.out.println("处于运行状态...甚么也不做"); } @Override public void stop() { System.out.println("处于运行状态:停止"); lift.setState(lift.getStopState()); } }

StopState:

public class StopState extends LiftState { public StopState(Lift lift) { super(lift); } @Override public void open() { System.out.println("处于停止状态:开门"); lift.setState(lift.getOpenState()); } @Override public void close() { System.out.println("处于停止状态...甚么也不做"); } @Override public void run() { System.out.println("处于停止状态:运行"); lift.setState(lift.getRunState()); } @Override public void stop() { System.out.println("处于停止状态...甚么也不做"); } }

Lift:

public class Lift { LiftState openState; LiftState closeState; LiftState runState; LiftState stopState; LiftState state; public Lift() { openState = new OpenState(this); closeState = new CloseState(this); runState = new RunState(this); stopState = new StopState(this); //起始设置为停止状态 state = stopState; } //拜托给状态对象履行 public void stop() { state.stop(); } public void run() { state.run(); } public void close() { state.close(); } public void open() { state.open(); } //仅仅更换当前对象的援用 public void setState(LiftState state) { this.state = state; } public LiftState getOpenState() { return openState; } public LiftState getCloseState() { return closeState; } public LiftState getRunState() { return runState; } public LiftState getStopState() { return stopState; } }

LiftRun:

public class LiftRun { public static void main(String[] args) { //其实设置为停止状态 Lift lift = new Lift(); lift.run(); lift.open(); lift.stop(); lift.close(); } }

运行结果:

状态模式

通过状态模式改进后,我们将每一个状态和状态行动封装成了对象和方法,虽然增加了类的数目,但是当我们增加状态时不用改动本来的代码,仅需创建新的状态实现接口的方法便可。

如增加故障状态:

public class BugState extends LiftState { public BugState(Lift lift) { super(lift); } @Override public void open() { System.out.println("故障状态,不能打开"); } @Override public void close() { System.out.println("故障状态,不能关闭"); } @Override public void run() { System.out.println("故障状态,不能运行"); } @Override public void stop() { System.out.println("故障状态,停止..维修"); lift.setState(lift.getStopState()); } @Override public void bug() { System.out.println("故障状态"); } }

在每一个状态对象中加入相应的 bug 状态函数,如:

@Override public void bug() { System.out.println("处于停止状态:维修电梯"); lift.setState(lift.getBugState()); }

接着在 Lift 类中加入 bug 状态:

LiftState bugState; ... //构造函数中 bugState = new BugState(this); ... public LiftState getBugState() { return bugState; }

测试函数:

public class LiftRun { public static void main(String[] args) { //其实设置为停止状态 Lift lift = new Lift(); //运行 lift.run(); lift.bug(); lift.stop(); lift.run(); } }

状态模式

处于状态模式下仅仅需要新增1个类外加做1些小改动便可完成状态的增加,大大方便了后期的保护和修改。

适用处景:

  • 1个对象的行动取决于它的状态, 并且它必须在运行时刻根据状态改变它的行动。
  • 代码中包括大量与对象状态有关的条件语句:1个操作中含有庞大的多分支的条件(if else 或switch case)语句,且这些分支依赖于该对象的状态。这个状态通经常使用1个或多个枚举常量表示。通常 , 有多个操作包括这1相同的条件结构。 State模式将每个条件分支放入1个独立的类中。这使得你可以根据对象本身的情况将对象的状态作为1个对象,这1对象可以不依赖于其他对象而独立变化。

优缺点:

优点:

  • 封装了转换规则,将所有与某个状态有关的行动放到1个类中,并且可以方便地增加新的状态,只需要改变对象状态便可改变对象的行动。
  • 允许状态转换逻辑与状态对象合成1体,而不是某1个巨大的条件语句块。
  • 可让多个环境对象同享1个状态对象,从而减少系统中对象的个数。

缺点:

  • 状态模式的使用必定会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将致使程序结构和代码的混乱。
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐