一:状态模式定义
--->当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类
--->状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样
--->状态模式相对来说比较复杂,它提供了一种对物质运动的另一个观察视角,通过状态变更促使行为的变化,就类似水的状态变更一样,一碗水的初始状态是液态,通过加热转变为气态,状态的改变同时也引起体积的扩大,然后就产生了一个新的行为:鸣笛或顶起壶盖,瓦特就是这么发明蒸汽机的
二:状态模式角色
● State——抽象状态角色接口或抽象类
负责对象状态定义,并且封装环境角色以实现状态切换。
● ConcreteState——具体状态角色
每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。
● Context——环境角色
定义客户端需要的接口,并且负责具体状态的切换。
【*****】环境角色不成文的约束
● 把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。
● 环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。
三:状态模式的应用
【1】状态模式的优点
● 结构清晰
避免了过多的switch...case或者if...else语句的使用,避免了程序的复杂性,提高系统的可维护性。
● 遵循设计原则
很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。
● 封装性非常好
这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。
【2】状态模式的缺点
状态模式既然有优点,那当然有缺点了。但只有一个缺点,子类会太多,也就是类膨胀。如果一个事物有很多个状态也不稀奇,如果完全使用状态模式就会有太多的子类,不好管理,这个需要大家在项目中自己衡量。其实有很多方式可以解决这个状态问题,如在数据库中建立一个状态表,然后根据状态执行相应的操作,这个也不复杂,看大家的习惯和嗜好了。
【3】状态模式的使用场景
● 行为随状态改变而改变的场景
这也是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式。
● 条件、分支判断语句的替代者
在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,它通过扩展子类实现了条件的判断处理。
【4】状态模式的注意事项
状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个
四:状态模式的案例
【1】电梯四个状态所作的固定的反应
【1】电梯的状态抽象
1 package com.yeepay.sxf.template21; 2 /** 3 * 抽象电梯状态 4 * 5 * (1)依赖:环境角色 6 * (2)不同状态下的行为 7 * @author sxf 8 * 9 */ 10 public abstract class LiftState { 11 12 //定义一个环境角色,也就是封装状态的变化引起功能的变化 13 protected Context context; 14 15 public void setContext(Context context){ 16 this.context=context; 17 } 18 //电梯的开启门行为 19 public abstract void open(); 20 //电梯的关闭们行为 21 public abstract void close(); 22 //电梯要能上能下,运行起来的行为 23 public abstract void run(); 24 //电梯停下来的行为 25 public abstract void stop(); 26 }
【2】环境角色
1 package com.yeepay.sxf.template21; 2 /** 3 * 环境类(上下文类) 4 * (1)依赖:电梯的状态 5 * (2)同时具有电梯不同状态的行为,类似代理 6 * @author sxf 7 * 8 */ 9 public class Context { 10 11 //定义出电梯所有的状态 12 public final static OpenningState openningState=new OpenningState(); 13 public final static ClosingState closingState=new ClosingState(); 14 public final static RunningState runningState=new RunningState(); 15 public final static StoppingState stoppingState=new StoppingState(); 16 17 //定义一个当前的电梯状态 18 private LiftState liftState; 19 20 public LiftState getLiftState(){ 21 return this.liftState; 22 } 23 24 public void setLiftState(LiftState liftState){ 25 this.liftState=liftState; 26 this.liftState.setContext(this); 27 } 28 //开启电梯门 29 public void open(){ 30 this.liftState.open(); 31 } 32 //关闭电梯门 33 public void close(){ 34 this.liftState.close(); 35 } 36 //停止电梯 37 public void stop(){ 38 this.liftState.stop(); 39 } 40 //运行电梯 41 public void run(){ 42 this.liftState.run(); 43 } 44 }
【3】电梯开启状态的实现类
1 package com.yeepay.sxf.template21; 2 /** 3 * 电梯开启状态 4 * @author sxf 5 * 6 */ 7 public class OpenningState extends LiftState { 8 9 10 //开启当然可以关闭了 11 @Override 12 public void open() { 13 System.out.println("OpenningState.open(电梯门开启......)"); 14 } 15 16 @Override 17 public void close() { 18 //修改状态 19 super.context.setLiftState(Context.closingState); 20 //动作委托为CloseState来执行 21 super.context.getLiftState().close(); 22 23 } 24 25 //电梯门若是开着,则不能运行 26 @Override 27 public void run() { 28 //do nothing; 29 } 30 //电梯门是开着,则不能让其停止 31 @Override 32 public void stop() { 33 //do nothing 34 } 35 36 37 }
【4】电梯停止状态的实现类
1 package com.yeepay.sxf.template21; 2 /** 3 * 电梯停止的行为 4 * @author sxf 5 * 6 */ 7 public class StoppingState extends LiftState { 8 9 10 11 //电梯处于停止状态是可以打开的 12 @Override 13 public void open() { 14 //将电梯的状态设置为打开 15 super.context.setLiftState(Context.openningState); 16 //执行打开的动作 17 super.context.getLiftState().open(); 18 } 19 20 //电梯处于停止状态,门本来就关着 21 @Override 22 public void close() { 23 //do nothing 24 } 25 26 //电梯处于停止状态,再次运行起来很正常 27 @Override 28 public void run() { 29 //将电梯的状态设置为运行状态 30 super.context.setLiftState(Context.runningState); 31 //执行运行动作 32 super.context.getLiftState().run(); 33 } 34 35 //停止状态要实现的行为 36 @Override 37 public void stop() { 38 System.out.println("StoppingState.stop(电梯停止了.....)"); 39 } 40 41 42 }
【5】电梯运行状态的实现类
1 package com.yeepay.sxf.template21; 2 /** 3 * 电梯运行时的状态 4 * @author sxf 5 * 6 */ 7 public class RunningState extends LiftState{ 8 9 //运行时,电梯门不允许打开。 10 @Override 11 public void open() { 12 //do nothing 13 } 14 //运行时,电梯门关闭,这是正常的。 15 @Override 16 public void close() { 17 //do nothing 18 } 19 20 //运行状态下,要实现的行为 21 @Override 22 public void run() { 23 System.out.println("RunningState.run(电梯上下运动.....)"); 24 } 25 26 //这是合理的。运行时,是可以停止的 27 @Override 28 public void stop() { 29 //将电梯的状态设置为停止状态 30 super.context.setLiftState(context.stoppingState); 31 //执行停止的动作 32 super.context.getLiftState().stop(); 33 } 34 35 36 }
【6】电梯关闭状态的实现类
1 package com.yeepay.sxf.template21; 2 /** 3 * 电梯关闭的状态 4 * @author sxf 5 * 6 */ 7 public class ClosingState extends LiftState { 8 9 10 //电梯门关了再打开 11 @Override 12 public void open() { 13 //将电梯门的状态该为打开状态 14 super.context.setLiftState(Context.openningState); 15 //执行相应的动作 16 super.context.getLiftState().open(); 17 } 18 19 //电梯们关闭,这是关闭状态要实现的动作 20 @Override 21 public void close() { 22 System.out.println("ClosingState.close(电梯门关闭.....)"); 23 } 24 25 //电梯门关了就运行,这是再正常不过了 26 @Override 27 public void run() { 28 //将电梯门的状态改为运行状态 29 super.context.setLiftState(context.runningState); 30 //执行运行状态的动作 31 super.context.getLiftState().run(); 32 } 33 34 //电梯门关着,我就不按楼层 35 @Override 36 public void stop() { 37 //将电梯门的状态改为停止状态 38 super.context.setLiftState(context.stoppingState); 39 //执行停止动作 40 super.context.getLiftState().stop(); 41 } 42 43 44 }
【4】客户端测试
1 package com.yeepay.sxf.template21; 2 /** 3 * 客户端测试 4 * @author sxf 5 * 6 */ 7 public class ClientTest { 8 9 public static void main(String[] args) { 10 //初始化上下文环境 11 Context context=new Context(); 12 //设置初始化状态 13 context.setLiftState(Context.closingState); 14 //打开门 15 context.open(); 16 //运行 17 context.run(); 18 //关闭门 19 context.close(); 20 //停止门 21 context.stop(); 22 23 } 24 }