当一个对象内在状态改变时允许其改变行为,这个对象看起来像是改变了其类 。 Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. [GoF, p305],也就是说状态模式封装的非常好,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
细想状态变更的实质就是一个状态与状态二维的关系表。电梯例子,通过状态的变化形成二维的表格,电梯门关闭、电梯门打开、电梯上下运载、电梯停止。
如果通过一个个遍历状态通过if esle 完成代码不利于代码维护。
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 Context {
// 定义出所有的电梯状态
public final static OpenningState openningState = new OpenningState();
public final static ClosingState closeingState = new ClosingState();
public final static RunningState runningState = new RunningState();
public final static StoppingState stoppingState = 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 run() {
this.liftState.run();
}
public void stop() {
this.liftState.stop();
}
}
public class ClosingState extends LiftState {
//电梯门关闭,这是关闭状态要实现的动作
@Override
public void close() {
System.out.println("电梯门关闭...");
}
//电梯门关了再打开,逗你玩呢,那这个允许呀
@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() {
super.context.setLiftState(Context.stoppingState); //设置为停止状态;
super.context.getLiftState().stop();
}
}
public class RunningState extends LiftState {
//电梯门关闭?这是肯定了
@Override
public void close() {
//do nothing
}
//运行的时候开电梯门?你疯了!电梯不会给你开的
@Override
public void open() {
//do nothing
}
//这是在运行状态下要实现的方法
@Override
public void run() {
System.out.println("电梯上下跑...");
}
//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState); //环境设置为停止状态;
super.context.getLiftState().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();
}
}
优点:
首先是避免了过多的 swith…case或者if..else语句的使用,避免了程序的复杂性;
其次是很好的使用体现了开闭原则和单一职责原则,每个状态都是一个子类,增加状态就增加子类,你要修改状态,你只修改一个子类就可以了;
最后一个好处就是封装性非常好,这也是状态模式的基本要求,状态变换放置到了类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。
缺点:子类会太多,也就是类膨胀。
工作流中 Activity(节点)有初始化状态(Initialized State)、挂起状态(Suspended State)、完成状态(Completed State)等等,流程实例这么多状态,状态怎么管理呢?通过状态机(State Machine)来管理, 状态机就是 Context类的扩展版!