Java中的状态模式总结

1|0综述

本文总结了状态模式的定义,特点,使用场景以及实现思路。

2|0状态模式的定义

状态模式为23种设计模式之中应用比较少见的模式,这并不是因为其适用范围过于狭窄,而是由于其兄弟模式“策略模式”实在过于强大了,但本文只关注状态模式,这里提一句以供参考。
状态模式是一种简化多步骤流程的类的模式。如果一个类提供一个具有多个步骤的流程,比如要达成目标A,需要通过步骤1,步骤2,步骤3完成。它就适合使用状态模式。
状态模式通过将多个步骤对应的方法和运行相应方法所需要的参数抽象为单独的状态,然后将状态对应的方法和参数变化分散到每个状态对应的状态实现类中,使得变更对象状态时直接变更负责的状态实现类,从而不再需要修改对象的行为,简化条件判断语句,提高代码可读性和可维护性,减少逻辑错漏。

3|0状态模式的特点和使用场合

状态模式具有如下优点:

结构清晰、封装性好:将状态的转换逻辑分布到独立的状态类中,使得状态之间的耦合度降低,并且可以将状态的行为封装在状态类中,提高了系统的可维护性和可读性。
易于维护和调试:状态模式将各个状态进行了封装,每个状态对象都只关注自身的行为,使得代码易于维护和调试。

但是状态模式也存在一些缺点:

状态模式会导致系统中类和对象的个数增加:状态模式将每个状态都封装成了独立的对象,因此会增加系统的复杂度。
状态模式违背开闭原则:由于状态模式将多个状态视为一个整体,状态之间的切换逻辑封装在内部,使得其在需要变更流程的时候必须修改原有的代码,违背了开闭原则。这也是它不如“策略模式”受欢迎的原因。

4|0状态模式的实现思路:

下图为状态模式的示意图

根据 GoF 的定义,状态模式的三个核心角色分别是:

上下文(Context):在有的地方又被翻译为环境,它定义了调用者所需要的方法,在任意时间点维护一个流程当前的状态,并将不同具体状态类中的方法封装统一对外提供。
抽象状态(State):它定义了一个接口,用于封装上下文对象中不同状态对应的行为。有的变种版本会选择使用抽象类来实现抽象状态。
具体状态(Concrete State):即前文所指的状态实现类,它实现了抽象状态接口或作为抽象类的实现类,封装了不同状态中上下文对象的具体行为。

具体实现时可以按照以下步骤实现

  1. 定义抽象状态接口或者实现类(State),它定义了在流程中上下文在每个具体状态下应该具有的行为。
  2. 定义具体状态类(ConcreteState1、ConcreteState2等),它们实现了抽象状态接口,封装了具体的状态行为。
  3. 定义上下文类/环境类(Context),它包含了当前状态并将不同状态下的行为封装成统一的方法对外提供。
  4. 让上下文类持有抽象状态的类型的对象,并将抽象状态的方法封装成统一的方法对外提供调用。

5|0实现状态模式的最小示例

下面展示一个实现状态模式的最小示例, 如果从精炼角度出发可能还能继续精炼, 但是当前已经足够帮助理解.

首先是一个对外提供服务的StateContext, 其他的类可以看作是context的内部组件

public class StateContext { private State state; // private StateFlag stateFlag; public StateContext(){ this.state = new StepOneStateImpl(); this.state.setContext(this); } public StateContext(StateFlag flag) { if (flag == StateFlag.NOT_START) { this.state = new StepOneStateImpl(); this.state.setContext(this); } if (flag.equals(StateFlag.FINISHED_STEP_ONE)){ this.state = new StepTwoStateImpl(); this.state.setContext(this); } } public void setState(State state){ this.state = state; // this.stateFlag = state.getState(); } public StateFlag getCurrentState(){ if (state != null) { return state.getState(); } throw new IllegalStateException("当前错误的处于未定义状态"); } public void nextStep() { state.doSomething(); } }

然后是State接口以及配合的AbstractState抽象类, 两者各有优势, 所以示例中同时用了

public interface State { void setContext(StateContext context); void doSomething(); StateFlag getState(); } public abstract class AbstractState implements State{ protected StateContext context; @Override public void setContext(StateContext context){ this.context = context; } @Override public abstract void doSomething(); @Override public abstract StateFlag getState(); }

然后是标记状态的枚举类, 这里为了让其同步输出log将枚举类写得功能多一些, 实际使用可以简化后放在context中作为内部类

public enum StateFlag { NOT_START("Not Start"), FINISHED_STEP_ONE("Finished Step One"); private final String stateString; StateFlag(String s) { this.stateString = s; } public String getStateString(){ return stateString; } }

接着是具体实现状态下行为的ConcreteState类, 或者称为状态行为类

public class StepOneStateImpl extends AbstractState{ public static final StateFlag state = StateFlag.NOT_START; @Override public void doSomething() { System.out.println("执行第一步"); if (context == null){ throw new RuntimeException("初始化state时未设置stateImpl中的context实例"); } StepTwoStateImpl stepTwoState = new StepTwoStateImpl(); stepTwoState.setContext(context); context.setState(stepTwoState); } @Override public StateFlag getState() { return state; } } public class StepTwoStateImpl extends AbstractState { public static final StateFlag state = StateFlag.FINISHED_STEP_ONE; @Override public void doSomething() { System.out.println("执行第二步"); //切换回原来的状态 StepOneStateImpl stepOneState = new StepOneStateImpl(); stepOneState.setContext(context); context.setState(stepOneState); } @Override public StateFlag getState() { return state; } }

最后, 如果需要调用这个模式进行测试的话在Main方法中调用即可

public class Main { public static void main(String[] args) { System.out.print("Hello and welcome!\n"); StateContext stateContext = new StateContext(); System.out.println("未执行nextStep的状态:" + stateContext.getCurrentState().getStateString()); stateContext.nextStep(); System.out.println("执行1次nextStep后的状态: " + stateContext.getCurrentState().getStateString()); stateContext.nextStep(); System.out.println("执行2次nextStep后的状态: " + stateContext.getCurrentState().getStateString()); } }

6|0状态模式拓展

前面已经提到, 状态模式属于违背开闭原则这个设计模式的总原则的模式, 同时状态模式还可以部分的被策略模式替代. 在种种不利的前提下, GoF仍然选择将状态模式加入23种设计模式中加以介绍. 我认为状态模式无法被替代的那部分功用一定相当重要.

那么仔细思考, 状态模式之所以违背开闭原则, 核心点在于其用来实现不同状态下表现的ConcreteState类之间的切换遵守严格的切换顺序, 而且这个切换顺序无法任意定制, 也无法从ConcreteState类之间独立出来, 因为这个切换顺序是context想要表现的这个类的内置属性而不是可以从外部接收的属性. 这就导致ConcreteState类和context类以及state接口之间的耦合是比较强的.

那么鉴于这是其基本特性, 而且状态模式本来就无法完全遵循开闭原则, 所以考虑舍弃兼顾开闭原则的思路转而放大其特性. 同时尽量按照易用性和易读性进行优化.

首先拓展版本也需要准备一个extStateContext

public class ExtStateContext { //当前state private ExtState currentConcreteState; private ExtState.ExtStateFLag currentStateFlag; //所有state private final ExtState concreteStateOne; private final ExtState concreteStateTwo; public ExtStateContext() { this.concreteStateOne = new ExtConcreteStateOne(); this.concreteStateTwo = new ExtConcreteStateTwo(); this.setCurrentConcreteState(concreteStateOne); } public void stepOneExecute(){ this.currentConcreteState.stepOneExecute(() -> this.setCurrentConcreteState(concreteStateTwo)); } public void stepTwoExecute(){ this.currentConcreteState.stepTwoExecute(() -> this.setCurrentConcreteState(concreteStateOne)); } public void resetFlow(){ this.currentConcreteState.resetFlow(() -> this.setCurrentConcreteState(concreteStateOne)); } public ExtState.ExtStateFLag getCurrentState(){ return this.currentStateFlag; } private void setCurrentConcreteState(ExtState concreteState){ this.currentConcreteState = concreteState; this.currentStateFlag = concreteState.getState(); } }

然后准备接口

public interface ExtState { void stepOneExecute(Callback callback); void stepTwoExecute(Callback callback); void resetFlow(Callback callback); ExtState.ExtStateFLag getState(); enum ExtStateFLag{ FLOW_NOT_START("flow not start"), FLOW_STEP_ONE_FINISHED("flow step one finished"); private final String stateString; ExtStateFLag(String stateString) { this.stateString = stateString; } public String getStateString() { return stateString; } } interface Callback{ void onCallback(); } }

然后是两个具体实现状态下行为的ExtConcreteState实现类

public class ExtConcreteStateOne implements ExtState{ //存放静态的状态flag private static final ExtState.ExtStateFLag extStateFLag = ExtStateFLag.FLOW_NOT_START; @Override public void stepOneExecute(Callback callback) { System.out.println("ExtConcreteStateOne, 执行第一步"); callback.onCallback(); } @Override public void stepTwoExecute(Callback callback) { throw new IllegalStateException("当前处于第一步尚未执行的状态, 无法执行第二步"); } @Override public void resetFlow(Callback callback) { System.out.println("ExtConcreteStateOne, 本身处于第一步尚未执行状态, 所以不重置, 甚至不切换"); } @Override public ExtState.ExtStateFLag getState() { return extStateFLag; } } public class ExtConcreteStateTwo implements ExtState{ //存放静态的状态flag private static final ExtState.ExtStateFLag extStateFLag = ExtStateFLag.FLOW_STEP_ONE_FINISHED; @Override public void stepOneExecute(Callback callback) { throw new IllegalStateException("当前处于第一步已执行第二步尚未执行的状态, 无法重复执行第一步"); } @Override public void stepTwoExecute(Callback callback) { System.out.println("ExtConcreteStateTwo, 执行第二步"); callback.onCallback(); } @Override public void resetFlow(Callback callback) { System.out.println("ExtConcreteStateTwo, 执行流程复位"); callback.onCallback(); } @Override public ExtStateFLag getState() { return extStateFLag; } }

最后, 给出测试用的方法

public class Main { public static void main(String[] args) { System.out.print("Hello and welcome!\n"); ExtStateContext extStateContext = new ExtStateContext(); System.out.println("未执行flow的状态:" + extStateContext.getCurrentState().getStateString()); extStateContext.stepOneExecute(); System.out.println("执行flow第一步的状态:" + extStateContext.getCurrentState().getStateString()); extStateContext.stepTwoExecute(); System.out.println("执行flow第二步的状态:" + extStateContext.getCurrentState().getStateString()); extStateContext.stepOneExecute(); extStateContext.resetFlow(); System.out.println("执行flow第一步后执行reset的状态:" + extStateContext.getCurrentState().getStateString()); } }

7|0总结

在拓展的变种模式中, 通过拆分每一步的方法, 并引入回调方法, 将状态转换操作集中到一个类中, 避免了context和concreteState之间的互相依赖, 提高代码的可读性和可维护性. 虽然在开闭原则上反而是后退了, 但是其他方面的优化盖过了这一点. 个人以为是更好的做法.


__EOF__

本文作者地维藏光
本文链接https://www.cnblogs.com/dwcg/p/17943548.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   地维藏光  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示