状态这个词汇咱们并不陌生,在平常生活中,不一样时间就有不一样的状态,早上起来精神饱满,中文想睡觉,下午又渐渐恢复,晚上可能精神更旺也可能耗费体力只想睡觉,这一天中就对应着不一样的状态。或者对软件开发人员更形象的描述多是UML的状态图(即用于描述一个实体基于事件反应的动态行为,显示了该实体如何根据当前所处的状态对不一样的事件作出反应)。git
其实相对来讲,就是一种状态的变化,而状态模式主要解决的问题就是当控制一个对象状态转换的条件表达式过于复杂时的状况。即把状态的判断逻辑转移到标识不一样状态的一系列类当中。ide
状态模式(State),当一个对象的内在状态改变时容许改变其行为,这个对象看起来像是改变了其类。UML结构图以下:学习
其中,Context类为环境角色,用于维护一个ConcreteState子类的实例,这个实例定义当前的状态;State为抽象状态角色,定义一个接口以封装与Context的一个特定接口状态相关的行为;ConcreteState是具体状态角色,每个子类实现一个与Context的一个状态相关的行为。this
环境角色具备两个职责,即处理本状态必须完成的任务,及决定是否能够过渡到其它状态。对于环境角色,有几个不成文的约束:spa
1 public class Context { 2 3 //定义状态 4 public final static State STATE1 = new ConcreteState1(); 5 public final static State STATE2 = new ConcreteState2(); 6 7 //当前状态 8 private State currentState; 9 10 //得到当前状态 11 public State getCurrentState() { 12 return currentState; 13 } 14 15 //设置当前状态 16 public void setCurrentState(State currentState) { 17 this.currentState = currentState; 18 // System.out.println("当前状态:" + currentState); 19 //切换状态 20 this.currentState.setContext(this); 21 } 22 23 public void handle1() { 24 this.currentState.handle1(); 25 } 26 public void handle2() { 27 this.currentState.handle2(); 28 } 29 30 }
抽象环境中声明一个环境角色,提供各个状态类自行访问,而且提供全部状态的抽象行为,由各个实现类实现。3d
1 public abstract class State { 2 3 protected Context context; 4 public void setContext(Context context) { 5 this.context = context; 6 } 7 8 //行为1 9 public abstract void handle1(); 10 //行为2 11 public abstract void handle2(); 12 13 }
具体状态实现,这里以定义ConcreteState1和ConcreteState2两个具体状态类为例,ConcreteState2的具体内容同ConcreteState1。code
1 public class ConcreteState1 extends State { 2 3 @Override 4 public void handle1() { 5 //... 6 System.out.println("ConcreteState1 的 handle1 方法"); 7 } 8 9 @Override 10 public void handle2() { 11 super.context.setCurrentState(Context.STATE2); 12 System.out.println("ConcreteState1 的 handle2 方法"); 13 } 14 15 }
定义Context环境角色,初始化具体状态1,执行行为观察结果。对象
1 public class Client { 2 3 public static void main(String[] args) { 4 //定义环境角色 5 Context context = new Context(); 6 //初始化状态 7 context.setCurrentState(new ConcreteState1()); 8 //行为执行 9 context.handle1(); 10 context.handle2(); 11 } 12 13 }
运行结果以下:blog
从运行结果可见,咱们已经隐藏了状态的变化过程,它的切换引发了行为的变化。对外来讲,咱们只看到了行为的改变,而不用知道是状态变化引发的。接口
若是仍是有点不理解的话,参考一下下方第三部分的内容,状态模式的具体实例便可。
咱们常常坐电梯都知道,电梯有多种状态,就按最简单的来讲,包括运行状态、中止状态、开门状态、闭门状态。下面就以电梯运行为例,举一个具体的实例,UML图以下:
首先定义出电梯的全部状态,而后定义当前电梯状态,再定义四种状态对应的方法,如Openning状态是由open()方法产生的。至于这些方法中的逻辑,就用print来代替了。
1 public class Context { 2 3 //定义出电梯的全部状态 4 public final static LiftState OPENNING_STATE = new OpenningState(); 5 public final static LiftState CLOSING_STATE = new ClosingState(); 6 public final static LiftState RUNNING_STATE = new RunningState(); 7 public final static LiftState STOPPING_STATE = new StoppingState(); 8 9 //定义一个当前电梯状态 10 private LiftState liftState; 11 12 public LiftState getLiftState() { 13 return liftState; 14 } 15 16 public void setLiftState(LiftState liftState) { 17 this.liftState = liftState; 18 //通知到各个实现类中 19 this.liftState.setContext(this); 20 } 21 22 public void open() { 23 this.liftState.open(); 24 } 25 26 public void close() { 27 this.liftState.close(); 28 } 29 30 public void run() { 31 this.liftState.run(); 32 } 33 34 public void stop() { 35 this.liftState.stop(); 36 } 37 }
这里咱们定义并把Context这个环境角色聚合进来,并传递到子类。因此咱们能够这样理解,Context环境角色的做用就是串联各个状态的过渡,也就是在4个具体的实现类中,各自根据本身的环境来决定如何进行状态的过渡。
1 public abstract class LiftState { 2 3 protected Context context; 4 5 public void setContext(Context context) { 6 this.context = context; 7 } 8 9 //电梯门开启动做 10 public abstract void open(); 11 //电梯门关闭动做 12 public abstract void close(); 13 //电梯运行 14 public abstract void run(); 15 //电梯中止 16 public abstract void stop(); 17 18 }
对于开门状态,除去自身的开启电梯门的方法以外,在打开门以后应该还具有关闭电梯门的功能,而门开着的时候是不能运行也不能中止的。
1 public class OpenningState extends LiftState { 2 3 //执行打开电梯门方法 4 @Override 5 public void open() { 6 System.out.println("电梯门开启"); 7 } 8 9 //打开后还能够关闭电梯门 10 @Override 11 public void close() { 12 //状态修改 13 super.context.setLiftState(Context.CLOSING_STATE); 14 //动做委托为CLOSING_STATE执行 15 super.context.getLiftState().close(); 16 } 17 18 //门开着不能运行 19 @Override 20 public void run() { 21 //什么都不作 22 } 23 24 //门开着已经中止了 25 @Override 26 public void stop() { 27 //什么都不作 28 } 29 30 }
对于闭门状态,除去自身外,电梯门关闭以后还能够再度打开,因此有open()方法;而门关了以后是能够运行的,因此有run()方法;若是关了门没有按楼层的话,此时电梯处于中止状态,因此有stop()方法。
1 public class ClosingState extends LiftState { 2 3 //电梯门关了能够再开 4 @Override 5 public void open() { 6 //置为敞门状态 7 super.context.setLiftState(Context.OPENNING_STATE); 8 super.context.getLiftState().open(); 9 } 10 11 // * 执行电梯门关闭方法 12 @Override 13 public void close() { 14 System.out.println("电梯门关闭"); 15 } 16 17 //电梯门关了就运行 18 @Override 19 public void run() { 20 super.context.setLiftState(Context.RUNNING_STATE); 21 super.context.getLiftState().run(); 22 } 23 24 //电梯门关了但没有按楼层 25 @Override 26 public void stop() { 27 super.context.setLiftState(Context.STOPPING_STATE); 28 super.context.getLiftState().stop(); 29 } 30 31 }
当电梯处于运行状态时,此时固然是不能开门的;而门确定是关了的,因此也没必要执行关门方法;此时电梯能够从运行状态转变为中止状态。
1 public class RunningState extends LiftState { 2 3 //运行时不能开门 4 @Override 5 public void open() { 6 //什么都不作 7 } 8 9 //运行时门确定是关的 10 @Override 11 public void close() { 12 //什么都不作 13 } 14 15 // * 执行运行方法 16 @Override 17 public void run() { 18 System.out.println("电梯运行中"); 19 } 20 21 //运行后能够中止 22 @Override 23 public void stop() { 24 //环境设置为中止状态 25 super.context.setLiftState(Context.STOPPING_STATE); 26 super.context.getLiftState().stop(); 27 } 28 29 }
当电梯处于中止状态时,门是关闭着的,因此不能执行关门的方法;但此时是能够开门的;而中止后电梯也能够再度运行,因此存在run()方法。
1 public class StoppingState extends LiftState { 2 3 //停下了要开门 4 @Override 5 public void open() { 6 super.context.setLiftState(Context.OPENNING_STATE); 7 super.context.getLiftState().open(); 8 } 9 10 //门原本就是关着的 11 @Override 12 public void close() { 13 //什么都不作 14 } 15 16 //中止后能够再运行 17 @Override 18 public void run() { 19 super.context.setLiftState(Context.RUNNING_STATE); 20 super.context.getLiftState().run(); 21 } 22 23 //执行中止方法 24 @Override 25 public void stop() { 26 System.out.println("电梯中止了"); 27 } 28 29 }
这里假设初始状态为闭门状态,你们能够自行尝试其它初始值。
1 public class Client { 2 3 public static void main(String[] args) { 4 Context context = new Context(); 5 6 //定义初始状态为关门(共四种初始值) 7 context.setLiftState(new ClosingState()); 8 context.open(); 9 context.close(); 10 context.run(); 11 context.stop(); 12 } 13 14 }
运行结果以下:
若是将setLiftState()方法的参数换成运行状态,即 context.setLiftState(new RunningState()); ,运行结果以下:
能够发现,此时电梯处于运行中,open()方法和close()方法都是无效的。
若是不用状态模式实现的话,经常使用的方法是在每一个方法中使用switch语句来判断当前状态进行处理,而使用状态模式,经过各个子类来实现,避免了switch语句的判断,使得代码看起来不是那么的冗杂。那么结合状态模式的特色,若是如今要增长两个状态即通电状态和断点状态呢?其实很简单,只需增长两个子类,并在原有类上增长而不是去修改,符合开闭原则;而这里咱们的状态都是单独的类,只有与这个状态有关的因素修改了,这个类才修改,符合迪米特法则。