状态模式【State Pattern 】
状态模式以电梯的状态转变为例。
先看下最初的类图设计。
当然这样子的设计实现起来很简单。如果再加上某个所处的状态能过渡到的状态呢?这时候就要考虑各个状态的相互转化了。
有了这张表再来设计一下类图:
定义如下的接口:
public interface ILift {
//电梯的四个状态
public final static int OPENING_STATE = 1; //门敞状态
public final static int CLOSING_STATE = 2; //门闭状态
public final static int RUNNING_STATE = 3; //运行状态
public final static int STOPPING_STATE = 4; //停止状态;
//设置电梯的状态
public void setState(int state);
//首先电梯门开启动作
public void open();
//电梯门有开启,那当然也就有关闭了
public void close();
//电梯要能上能下,跑起来
public void run();
//电梯还要能停下来,停不下来那就扯淡了
public void stop();
}
那么再实现类中我们可以根据电梯所处的不同状态进行大量的判断,举个栗子:电梯门开启时
//电梯门开启
public void open() {
//电梯在什么状态才能开启
switch(this.state){
case OPENING_STATE: //如果已经在门敞状态,则什么都不做
//do nothing;
break;
case CLOSING_STATE: //如是电梯时关闭状态,则可以开启
this.openWithoutLogic();
this.setState(OPENING_STATE);
break;
case RUNNING_STATE: //正在运行状态,则不能开门,什么都不做
//do nothing;
break;
case STOPPING_STATE: //停止状态,淡然要开门了
this.openWithoutLogic();
this.setState(OPENING_STATE);
break;
}
}
想想要是状态多的话,每个状态都要进行这样子的判断,那个代码量就真是不能见人了。还是得改善一下:
在类图中,定义了一个 LiftState 抽象类,声明了一个受保护的类型 Context 变量,这个是串联我们各个状态的封装类,封装的目的很明显,就是电梯对象内部状态的变化不被调用类知晓,也就是迪米特法则了,我的类内部情节你知道越少越好,并且还定义了四个具体的实现类,承担的是状态的产生以及状态间的转换过渡。
定义如下的抽象类:
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 run();
//电梯还要能停下来,停不下来那就扯淡了
public abstract void stop();
}
下面看一下电梯停止状态时能够干的事:
public class StoppingState extends LiftState {
//停止状态关门?电梯门本来就是关着的!
@Override
public void close() {
//do nothing;
}
//停止状态,开门,那是要的!
@Override
public void open() {
super.context.setLiftState(Context.openningState);
super.context.getLiftState().open();
}
//停止状态再跑起来,正常的很
@Override
public void run() {
super.context.setLiftState(Context.runningState);
super.context.getLiftState().run();
}
//停止状态是怎么发生的呢?当然是停止方法执行了
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
依次类推,就可以实现不同状态的业务逻辑了。
在主类中调用时:
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(new ClosingState());
context.open();
context.close();
context.run();
context.stop();
}
}
这样就不用考虑状态的变更了,显得比较清爽。
那什么是状态模式呢?就是当一个对象内在状态改变时允许其改变行为,这个对象看起来像是改变了其类。不理解?再看:也就是说状态模式封装的非常好,状态的变更
引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
其通用的类图如下:
总结:
优点:
(1)避免了过多的 swith…case 或者 if..else 语句的使用,避免了程序的复杂性;
(2)很好的使用体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就增加子类,你要修改状态,你只修改一个子类就可以了。
(3)封装性非常好,这也是状态模式的基本要求,状态变换放置到了类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。
缺点:
类膨胀,你想一个事物有七八、十来个状态也不稀奇,如果完全使用状态模式就会有太多的子类。
模式的选取:选取这个模式时也要考虑状态的个数。
最后:如果需要我们把已经有的几种状态按照一定的顺序再重新组装一下,那这个是什么模式?建造者模式!对,建造模式+状态模式会起到非常好的封装作用。就先这样了,以后得好好学一下模式的混编。