- 状态模式:当一个对象内在状态改变时允许改变行为,这个对象看起来像改变了其类(Allow an object to alter its behavior when its internal state changes. The object will appear to change its class)。
- 状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变。
本篇采用设计模式之禅中的实例,关于电梯状态改变,从而引发电梯行为改变的例子。我们首先对假设电梯有四种状态:敞门状态、闭门状态、运行状态、停止状态,电梯有四个动作:开门、关门、运行、停止,我们通过下表表示电梯状态和动作之间的关系:
|
开门(open) |
关门(close) |
运行(run) |
停止(stop) |
敞门状态 |
● |
★ |
● |
● |
闭门状态 |
★ |
● |
★ |
★ |
运行状态 |
● |
● |
● |
★ |
停止状态 |
★ |
● |
★ |
● |
电梯动作和状态对应表(●表示不允许,★表示允许)
当然我们在程序中可以通过设置电梯的状态,从而执行每个动作时判断当前电梯处于什么状态,从而决定电梯执行的动作,然而程序中会存在大量的switch......case语句。接下来我们直接介绍通过状态模式的方式实现电梯状态与动作的对应关系。
public abstract class LiftState {
private Context context;
public void setContext(Context text){
this.context = text;
}
public Context getContext(){
return this.context;
}
public abstract void open();
public abstract void close();
public abstract void run();
public abstract void stop();
}
public class OpenningState extends LiftState{
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println("电梯门开启......");
}
@Override
public void close() {
// TODO Auto-generated method stub
super.getContext().setLiftState(Context.closingState);
super.getContext().getLiftState().close();
}
@Override
public void run() {
// TODO Auto-generated method stub
}
@Override
public void stop() {
// TODO Auto-generated method stub
}
}
public class ClosingState extends LiftState{
@Override
public void open() {
// TODO Auto-generated method stub
super.getContext().setLiftState(Context.openningState);
super.getContext().getLiftState().open();
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println("电梯门关闭......");
}
@Override
public void run() {
// TODO Auto-generated method stub
super.getContext().setLiftState(Context.runningState);
super.getContext().getLiftState().run();
}
@Override
public void stop() {
// TODO Auto-generated method stub
super.getContext().setLiftState(Context.stoppingState);
super.getContext().getLiftState().stop();
}
}
public class RunningState extends LiftState{
@Override
public void open() {
// TODO Auto-generated method stub
}
@Override
public void close() {
// TODO Auto-generated method stub
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("电梯正在运行......");
}
@Override
public void stop() {
// TODO Auto-generated method stub
super.getContext().setLiftState(Context.stoppingState);
super.getContext().getLiftState().stop();
}
}
public class StoppingState extends LiftState{
@Override
public void open() {
// TODO Auto-generated method stub
super.getContext().setLiftState(Context.openningState);
super.getContext().getLiftState().open();
}
@Override
public void close() {
// TODO Auto-generated method stub
}
@Override
public void run() {
// TODO Auto-generated method stub
super.getContext().setLiftState(Context.runningState);
super.getContext().getLiftState().run();
}
@Override
public void stop() {
// TODO Auto-generated method stub
System.out.println("电梯停止了......");
}
}
public class Context {
public static OpenningState openningState = new OpenningState();
public static ClosingState closingState = new ClosingState();
public static RunningState runningState = new RunningState();
public static StoppingState stoppingState = new StoppingState();
private LiftState liftState; //定义一个电梯的状态
public void setLiftState(LiftState state){
this.liftState = state;
//把当前的环境通知到各个实现类中
this.liftState.setContext(this);
}
public LiftState getLiftState(){
return this.liftState;
}
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 Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Context context = new Context();
context.setLiftState(new ClosingState());
context.open();
context.close();
context.run();
context.stop();
}
}
View Code
- 状态模式的优点
- 结构清晰,避免了过多的switch...case或者if...else语句,避免了程序的复杂性,提高了程序的可维护性;
- 遵循设计原则,很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,增加状态就增加子类,修改状态只需修改状态对应的子类
- 封装性好,状态的变化放置到类的内部来实现,外部的调用不知道类内部如何实现状态和行为的变换。
- 状态模式的缺点
- 状态模式的缺点就是当对象状态过多时,会导致子类过多,也就是类膨胀,当然可以通过和其他设计模式组合(建造者模式)的方式来缓解类过多的情况。
- 行为虽状态改变而改变的场景,这也是状态模式的出发点,例如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式。
- 条件、分支判断语句的替代者,在程序中大量使用switch或者if判断语句导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,通过扩展子类实现了条件的判断逻辑。
- 在行为受状态约束的场景下可以使用状态模式,而且使用时对象的状态最好不要超过5个。