1. 简介
状态模式 (State Pattern), 当一个对象内在状态改变时允许其改变行为, 这个对象看起来像改变了其类. 简而言之, 就是状态的变更引起了行为的变更.
2. 图解
下图四辆汽车, 分别代表汽车日常的四种状态.
开门状态:
关门状态:
飞奔状态:
停止状态:
其中, 某种特定状态下, 都有四个可执行操作, 分别是 open,close,run,stop, 然后做对应的处理得下图所示.
3. 案例实现
类图如下:
定义汽车抽象状态类 CarState, 持有类型为 Context 的属性, 同时持有四个可执行操作, open,close,run,stop 方法;
定义汽车抽象状态类的子类 OpenningState,ClosingState,RunningState,StoppingState, 分别代表开门状态, 关门状态, 飞奔状态, 停止状态;
定义环境角色类 Context, 把状态对象声明为静态常量, 有几个状态对象就声明几个静态常量, 环境角色具有状态抽象角色定义的所有行为, 具体执行使用委托方式. 具体环境角色有两个职责: 处理本状态必须完成的任务, 决定是否可以过渡到其他状态.
代码实现如下:
- package com.wzj.state.example1;
- /**
- * @Author: wzj
- * @Date: 2019/11/3 20:10
- * @Desc: 汽车状态抽象类
- */
- public abstract class CarState {
- // 环境角色, 封装状态变化引起的行为变化
- 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();
- }
- package com.wzj.state.example1;
- /**
- * @Author: wzj
- * @Date: 2019/11/3 20:23
- * @Desc: 汽车开门状态类
- */
- public class OpenningState extends CarState {
- // 打开汽车门
- public void open() {
- System.out.println("汽车门已开");
- }
- // 关闭汽车门
- public void close() {
- // 状态修改
- super.context.setCarState(Context.closingState);
- // 动作委托为 ClosingState 来执行
- super.context.getCarState().close();
- }
- // 门开着时汽车一般不奔跑
- public void run() {
- System.out.println("汽车开门状态, 不能奔跑");
- }
- // 车门开着时, 切换不到停止状态, 因为没有四种状态中, 没有开门且停止这个状态
- public void stop() {
- System.out.println("汽车开门状态, 不能长时间开着门且处于停止状态");
- }
- }
- package com.wzj.state.example1;
- /**
- * @Author: wzj
- * @Date: 2019/11/3 20:23
- * @Desc: 汽车飞奔状态类
- */
- public class RunningState extends CarState {
- // 打开奔跑时不开门
- public void open() {
- System.out.println("车在飞奔, 不能打开");
- }
- // 奔跑时肯定是关门的
- public void close() {
- System.out.println("车在飞奔, 已经关闭, 不能再次关闭");
- }
- // 汽车在飞奔
- public void run() {
- System.out.println("汽车在飞奔");
- }
- // 汽车可以停下来
- public void stop() {
- // 修改汽车为停止状态
- super.context.setCarState(Context.stoppingState);
- // 停止动作委托为 StoppingState 类来执行
- super.context.getCarState().stop();
- }
- }
- package com.wzj.state.example1;
- /**
- * @Author: wzj
- * @Date: 2019/11/3 20:23
- * @Desc: 汽车关门状态类
- */
- public class ClosingState extends CarState {
- // 打开汽车门
- public void open() {
- // 修改汽车为开门状态
- super.context.setCarState(Context.openningState);
- // 动作委托为 OpenningState 类来执行
- super.context.getCarState().open();
- }
- // 关闭汽车门
- public void close() {
- System.out.println("汽车门已关");
- }
- // 汽车在飞奔
- public void run() {
- // 修改汽车为飞奔状态
- super.context.setCarState(Context.runningState);
- // 动作委托为 RunningState 类来执行
- super.context.getCarState().run();
- }
- // 汽车在停止
- public void stop() {
- // 设置汽车状态为停止状态
- super.context.setCarState(Context.stoppingState);
- // 动作委托为 StoppingState 类来执行
- super.context.getCarState().stop();
- }
- }
- package com.wzj.state.example1;
- /**
- * @Author: wzj
- * @Date: 2019/11/3 20:19
- * @Desc: 上下文环境类
- */
- public class Context {
- /** 列出汽车所有状态
- * openningState - 开门状态 closingState - 关门状态
- * runningState - 奔驰状态 stoppingState - 停止状态
- */
- public static final OpenningState openningState = new OpenningState();
- public static final ClosingState closingState = new ClosingState();
- public static final RunningState runningState = new RunningState();
- public static final StoppingState stoppingState = new StoppingState();
- // 定义汽车当前状态
- private CarState carState;
- public CarState getCarState() {
- return carState;
- }
- public void setCarState(CarState carState) {
- this.carState = carState;
- // 切换状态
- this.carState.setContext(this);
- }
- // 汽车开门
- public void open() {
- this.carState.open();
- }
- // 汽车关门
- public void close(){
- this.carState.close();
- }
- // 汽车飞奔
- public void run(){
- this.carState.run();
- }
- // 汽车停止
- public void stop(){
- this.carState.stop();
- }
- }
客户端类如下:
- package com.wzj.state.example1;
- /**
- * @Author: wzj
- * @Date: 2019/11/3 21:06
- * @Desc:
- */
- public class Client {
- public static void main(String[] args) {
- Context context = new Context();
- context.setCarState(new OpenningState());
- // context.setCarState(new ClosingState());
- // context.setCarState(new RunningState());
- // context.setCarState(new StoppingState());
- context.open();
- // context.close();
- // context.run();
- // context.stop();
- }
- }
执行结果如下:
当只打开 Client15 行的时候, 分别打开 11,12,13,14 行的代码, 会得到如下结果:
汽车为开门状态时, 执行 open
汽车为关门状态时, 执行 open
汽车为飞奔状态时, 执行 open
汽车为停止状态时, 执行 open
上述结果可以看出, 同样执行一个 open 方法, 当状态的变化时导致行为的变化.
4. 状态模式总结
优点
结构清晰
避免了过多的 switch...case 或者 if...else 语句的使用, 避免了程序的复杂性, 提高系统的可维护性;
遵循设计原则
很好地体现了开闭原则和单一职责原则, 每个状态都是一个子类, 你要增加状态就要增加子类, 你要修改状态, 你只修改一个子类就可以了.
封装性非常好
这也是状态模式的基本要求, 状态变换放置到类的内部来实现, 外部的调用不用知道类内部如何实现状态和行为的变换.
缺点
状态模式既然有优点, 那当然有缺点了. 但只有一个缺点, 子类会太多, 也就是类膨胀. 如果一个事物有很多个状态也不稀奇, 如果完全使用状态模式就会有太多的子类, 不好管理, 这个需要大家在项目中自己衡量. 其实有很多方式可以解决这个状态问题, 如在数据库中建立一个状态表, 然后根据状态执行相应的操作, 这个也不复杂, 看大家的习惯和嗜好了.
来源: https://www.cnblogs.com/father-of-little-pig/p/12130821.html