5.状态模式
(1).概述
通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态停止状态,运行状态
。每一种状态改变,都有可能要根据其他状态来更新处理。列如: 如果电梯门现在处于运行时状态,就不能进行开门
的操作,而如果电梯门是停止状态,就可以执行开门操作
。
- 没有使用状态模式前
接口: 在接口中直接定义变量是 :全局变量
package com.jsxs.behavioralModel.state.before; /** * @Author Jsxs * @Date 2023/4/23 16:10 * @PackageName:com.jsxs.behavioralModel.state.before * @ClassName: ILift * @Description: TODO 电梯接口 * @Version 1.0 */ public interface ILift { // 定义四个电梯状态的常量 int OPENING_STATE=1; int CLOSING_STATE=2; int RUNNING_STATE=3; int STOPPING_STATE=4; // 设置电梯状态的功能 void setState(int state); // 设置电梯操作的功能 void open(); void close(); void ran(); void stop(); }
实现类
package com.jsxs.behavioralModel.state.before; /** * @Author Jsxs * @Date 2023/4/23 16:14 * @PackageName:com.jsxs.behavioralModel.state.before * @ClassName: Lift * @Description: TODO 电梯类 (子类) * @Version 1.0 */ public class Lift implements ILift{ // 声明一个记录当前电梯状态的变量。 private int state; @Override public void setState(int state) { this.state=state; // 赋值 } @Override public void open() { switch (state){ //当前电梯状态 case OPENING_STATE: // 什么事情都不做 break; case CLOSING_STATE: System.out.println("电梯打开了"); setState(OPENING_STATE); break; case RUNNING_STATE: // 什么事情都不做 break; case STOPPING_STATE: System.out.println("电梯打开了"); setState(OPENING_STATE); break; } } @Override public void close() { switch (state){ //当前电梯状态 case OPENING_STATE: System.out.println("电梯关闭了"); setState(CLOSING_STATE); break; case CLOSING_STATE: break; case RUNNING_STATE: // 什么事情都不做 break; case STOPPING_STATE: break; } } @Override public void ran() { switch (state){ //当前电梯状态 case OPENING_STATE: // 什么事情都不做 break; case CLOSING_STATE: System.out.println("电梯开始运行了.."); setState(RUNNING_STATE); break; case RUNNING_STATE: // 什么事情都不做 break; case STOPPING_STATE: System.out.println("电梯开始运行了.."); setState(RUNNING_STATE); break; } } @Override public void stop() { switch (state){ //当前电梯状态 case OPENING_STATE: // 什么事情都不做 break; case CLOSING_STATE: System.out.println("电梯停止了"); setState(STOPPING_STATE); break; case RUNNING_STATE: System.out.println("电梯停止了"); setState(STOPPING_STATE); break; case STOPPING_STATE: break; } } }
测试
package com.jsxs.behavioralModel.state.before; /** * @Author Jsxs * @Date 2023/4/23 16:25 * @PackageName:com.jsxs.behavioralModel.state.before * @ClassName: Client * @Description: TODO * @Version 1.0 */ public class Client { public static void main(String[] args) { Lift lift = new Lift(); // 设置当前电梯的状态 lift.setState(ILift.OPENING_STATE); //开门 lift.open(); lift.close(); lift.ran(); lift.stop(); } }
问题分析:
- 使用了大量的switch…case这样的判断(if…else也是一样),实用程序的可阅读性变差。
- 扩展性很差。如果加了断电的状态,我们需要修改上面判断逻辑。
定义:
对有状态的对象,把复杂的"判断逻辑"提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
(2).结构
状态模式包含以下主要角色:
- 环境模式: 也称上下文,也定义了客户程序需要的接口,维护了一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
- 抽象状态角色: 定义一个
接口
,用以封装对象中的特定状态所对应的行为。 - 具体状态角色:
实现抽象状态
所对应的行为。
(3).案列实现
对上诉电梯的案列使用状态模式进行改进。类图如下:
抽象状态类
package com.jsxs.behavioralModel.state.after; /** * @Author Jsxs * @Date 2023/4/23 17:13 * @PackageName:com.jsxs.behavioralModel.state.after * @ClassName: LiftState * @Description: TODO 抽象状态类 * @Version 1.0 */ 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 ran(); // 电梯停止操作 public abstract void stop(); }
具体状态角色
package com.jsxs.behavioralModel.state.after; /** * @Author Jsxs * @Date 2023/4/23 17:16 * @PackageName:com.jsxs.behavioralModel.state.after * @ClassName: OpeningState * @Description: TODO 电梯开启状态类 * @Version 1.0 */ public class OpeningState extends LiftState{ @Override public void open() { System.out.println("电梯开启.."); } @Override public void close() { // 修改状态 super.context.setLiftState(Context.CLOSING_STATE); // 调用当前状态中的context中的close方法 super.context.close(); } @Override public void ran() { // 什么都不做 } @Override public void stop() { // 什么都不做 } }
package com.jsxs.behavioralModel.state.after; /** * @Author Jsxs * @Date 2023/4/23 17:18 * @PackageName:com.jsxs.behavioralModel.state.after * @ClassName: ClosingState * @Description: TODO 电梯关闭状态 * @Version 1.0 */ public class ClosingState extends LiftState{ @Override public void open() { // 修改状态 super.context.setLiftState(Context.OPENING_STATE); // 调用当前状态中的context中的close方法 super.context.open(); } @Override public void close() { System.out.println("电梯关闭.."); } @Override public void ran() { // 修改状态 super.context.setLiftState(Context.RUNNING_STATE); // 调用当前状态中的context中的close方法 super.context.ran(); } @Override public void stop() { // 修改状态 super.context.setLiftState(Context.STOPPING_STATE); // 调用当前状态中的context中的close方法 super.context.stop(); } }
package com.jsxs.behavioralModel.state.after; /** * @Author Jsxs * @Date 2023/4/23 17:19 * @PackageName:com.jsxs.behavioralModel.state.after * @ClassName: RunningState * @Description: TODO * @Version 1.0 */ public class RunningState extends LiftState{ @Override public void open() { } @Override public void close() { } @Override public void ran() { System.out.println("电梯运行..."); } @Override public void stop() { // 修改状态 super.context.setLiftState(Context.STOPPING_STATE); // 调用当前状态中的context中的close方法 super.context.stop(); } }
package com.jsxs.behavioralModel.state.after; /** * @Author Jsxs * @Date 2023/4/23 17:19 * @PackageName:com.jsxs.behavioralModel.state.after * @ClassName: StoppingState * @Description: TODO * @Version 1.0 */ public class StoppingState extends LiftState{ @Override public void open() { // 修改状态 super.context.setLiftState(Context.OPENING_STATE); // 调用当前状态中的context中的close方法 super.context.getLiftState().close(); } @Override public void close() { // 修改状态 super.context.setLiftState(Context.CLOSING_STATE); // 调用当前状态中的context中的close方法 super.context.getLiftState().close(); } @Override public void ran() { // 修改状态 super.context.setLiftState(Context.RUNNING_STATE); // 调用当前状态中的context中的close方法 super.context.getLiftState().ran(); } @Override public void stop() { System.out.println("电梯停止了..."); } }
环境角色
package com.jsxs.behavioralModel.state.after; /** * @Author Jsxs * @Date 2023/4/23 17:14 * @PackageName:com.jsxs.behavioralModel.state.after * @ClassName: Context * @Description: TODO 环境角色类 * @Version 1.0 */ public class Context { // 定义对应状态对象的常量 public final static OpeningState OPENING_STATE = new OpeningState(); public final static ClosingState CLOSING_STATE = new ClosingState(); public final static RunningState RUNNING_STATE = new RunningState(); public final static StoppingState STOPPING_STATE = new StoppingState(); //定义当前电梯状态变量 private LiftState liftState; public LiftState getLiftState() { return liftState; } // 设置当前状态对象 public void setLiftState(LiftState liftState) { this.liftState = liftState; // 设置当前状态对象中的Context对象 this.liftState.setContext(this); } // 电梯操作的方法 public void open() { this.liftState.open(); } public void close() { this.liftState.close(); } public void ran() { this.liftState.ran(); } public void stop() { this.liftState.stop(); } }
测试
package com.jsxs.behavioralModel.state.after; /** * @Author Jsxs * @Date 2023/4/23 17:37 * @PackageName:com.jsxs.behavioralModel.state.after * @ClassName: Client * @Description: TODO * @Version 1.0 */ public class Client { public static void main(String[] args) { // 创建环境角色 Context context = new Context(); context.setLiftState(new RunningState()); context.open(); context.close(); context.ran(); context.stop(); } }
(4).使用场景
优点:
- 将所有与某个状态有关的行为放到一个类中,并且可以方便的增加新的状态,只需要改变对象状态即可改变对象的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
缺点:
- 状态模式的使用必然会
增加系统类和对象的个数
。 - 状态模式的结构与实现都较为复杂,
如果使用不当将导致程序结构和代码的混乱
。 - 状态模式对"
开闭原则
"的支持不太友好。
使用场景:
当一个对象的行为取决于它的状态,并且他必须在运行时根据状态改变它的行为时
,就可以考虑使用状态模式。- 一个操作中含有庞大的分支结构,并且这些
分支决定对象的状态
。