Java设计模式:四、行为型模式-07:状态模式

文章目录

  • 一、定义:状态模式
  • 二、模拟场景:状态模式
    • 2.1 状态模式
    • 2.2 引入依赖
    • 2.3 工程结构
    • 2.4 模拟审核状态流转
      • 2.4.1 活动状态枚举
      • 2.4.2 活动信息类
      • 2.4.3 活动服务接口
      • 2.4.4 返回结果类
  • 三、违背方案:状态模式
    • 3.0 引入依赖
    • 3.1 工程结构
    • 3.2 活动执行状态变更控制层
    • 3.3 单元测试
  • 四、改善代码:状态模式
    • 4.0 引入依赖
    • 4.1 工程结构
    • 4.2 状态模式结构图
    • 4.3 活动状态变更流程
      • 4.3.1 定义状态抽象类
      • 4.3.2 待审核状态流转实现
      • 4.3.3 活动关闭状态流转实现
      • 4.3.4 活动中状态流转实现
      • 4.3.5 编辑中状态流转实现
      • 4.3.6 活动开启状态流转实现
      • 4.3.7 审核通过状态流转实现
      • 4.3.8 审核拒绝状态流转实现
      • 4.3.9 活动执行者
    • 4.4 单元测试
      • 4.4.1 编辑中到提审活动测试验证
      • 4.4.2 编辑中到开启活动测试验证
      • 4.4.3 审批拒绝到活动中测试验证
      • 4.4.4 审批拒绝到撤审测试验证
  • 五、总结:状态模式

一、定义:状态模式

请添加图片描述

  • **状态模式:**是一个行为下的多种状态变更。
    • 比如我们常见的一个网站的页面,在你登录与不登录下展示的内容是略有差异的 (不登录不能展示个人信息)
    • 这种 登录不登录 就是我们通过改变 状态,而让整个行为发生了变化。
  • 状态模式的使用场景。例如:磁带放音机。
    • 它的上面是一排按钮,当放入磁带后,通过上面的按钮就可以让放音机播放磁带上的内容。
    • 而且有些按钮是互斥的,当在某个状态下才可以按另外的按钮(这在设计模式里也是一个关键的点 )。

二、模拟场景:状态模式

2.1 状态模式

请添加图片描述

  • 模拟营销活动审核状态流转场景(一个活动的上线是多个层级审核上线的)。
  • 在上图中可以看到我们的流程节点中包括了各个状态到下一个状态扭转的关联条件。
    • 比如:审核通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是我们要完成的场景处理。
  • 我们都开发过类似的业务场景,需要对活动或者一些配置需要审核后才能对外发布,而这个审核的过程往往会随着系统的重要程序而设立多级控制,来保证一个活动可以安全上线,避免造成资损。
  • 当然有时候会用到一些审批流的过程配置,也是非常方便开发类似的流程的,也可以在配置中设定某个节点的审批人员。
  • 在本案例中们我主要是模拟学习对一个活动的多个状态节点的审核控制。

2.2 引入依赖

pom.xml

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><!-- LOGGING begin --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.5</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.0.9</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency>
</dependencies>

2.3 工程结构

design-20.0-0
|——src|——main|--java|--com.lino.design|-ActicityInfo.java|-ActivityService.java|-Result.java|-Status.java

2.4 模拟审核状态流转

2.4.1 活动状态枚举

Status.java

package com.lino.design;/*** @description: 状态枚举*/
public enum Status {/*** 1.创建编辑,2.待审核,3.审核通过(任务扫描成活动中),* 4.审核拒绝(可以撤审到编辑状态),5.活动中,* 6.活动关闭,7.活动开启(任务扫描成活动中)*/Editing, Check, Pass, Refuse, Doing, Close, Open
}

2.4.2 活动信息类

ActicityInfo.java

package com.lino.design;import java.util.Date;
import java.util.Map;/*** @description: 活动类*/
public class ActicityInfo {/*** 活动ID*/private String acticityId;/*** 活动名称*/private String acticityName;/*** 活动状态*/private Enum<Status> status;/*** 开始时间*/private Date beginTime;/*** 结束时间*/private Date endTime;public String getActicityId() {return acticityId;}public void setActicityId(String acticityId) {this.acticityId = acticityId;}public String getActicityName() {return acticityName;}public void setActicityName(String acticityName) {this.acticityName = acticityName;}public Enum<Status> getStatus() {return status;}public void setStatus(Enum<Status> status) {this.status = status;}public Date getBeginTime() {return beginTime;}public void setBeginTime(Date beginTime) {this.beginTime = beginTime;}public Date getEndTime() {return endTime;}public void setEndTime(Date endTime) {this.endTime = endTime;}
}
  • 基本的活动信息:活动ID、活动名称、活动状态、开始时间、结束时间

2.4.3 活动服务接口

ActivityService.java

package com.lino.design;import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @description: 活动服务类*/
public class ActivityService {private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<>();/*** 初始化活动** @param activityId 活动ID* @param status     活动状态*/public static void init(String activityId, Enum<Status> status) {// 模拟查询活动信息ActicityInfo acticityInfo = new ActicityInfo();acticityInfo.setActicityId(activityId);acticityInfo.setActicityName("早起学习打卡领奖活动");acticityInfo.setStatus(status);acticityInfo.setBeginTime(new Date());acticityInfo.setEndTime(new Date());statusMap.put(activityId, status);}/*** 查询活动信息** @param activityId 活动ID* @return 活动信息*/public static ActicityInfo queryActivityInfo(String activityId) {// 模拟查询活动信息ActicityInfo acticityInfo = new ActicityInfo();acticityInfo.setActicityId(activityId);acticityInfo.setActicityName("早起学习打卡领奖活动");acticityInfo.setStatus(statusMap.get(activityId));acticityInfo.setBeginTime(new Date());acticityInfo.setEndTime(new Date());return acticityInfo;}/*** 查询活动状态** @param activityId 活动ID* @return 活动状态*/public static Enum<Status> queryActivityStatus(String activityId) {return statusMap.get(activityId);}/*** 执行状态变更** @param activityId   活动ID* @param beforeStatus 变更前状态* @param afterStatus  变更后状态*/public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {if (!beforeStatus.equals(statusMap.get(activityId))) {return;}statusMap.put(activityId, afterStatus);}
}
  • 在这个静态类中提供了活动的查询和状态变更接口。
    • queryActivityInfo :查询活动信息。
    • queryActivityStatus :查询活动状态。
    • execStatus:执行状态变更。
  • 同时使用 Map 的结构来记录活动ID 和状态变化信息,另外还有 init 方法来初始化活动数据。
    • 实际的开发中这类信息基本都是从 数据库 或者 Redis 中获取。

2.4.4 返回结果类

Result.java

package com.lino.design;/*** @description: 结果类*/
public class Result {/*** 编码*/private String code;/*** 描述*/private String info;public Result(String code, String info) {this.code = code;this.info = info;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}
}

三、违背方案:状态模式

对于这样各种状态的变更,最让我们直接想到的就是使用 ifelse 进行判断处理。
每一个状态可以流转到下一个什么状态,都可以使用嵌套的 if 实现。

3.0 引入依赖

<dependencies><dependency><groupId>com.lino</groupId><artifactId>design-20.0-0</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>

3.1 工程结构

design-20.0-1
|——src|——main|--java|--com.lino.design|-ActivityExecStatusController.java|--test|--com.lino.design.test|-ApiTest.java

3.2 活动执行状态变更控制层

ActivityExecStatusController.java

package com.lino.design;/*** @description: 活动状态变更控制层*/
public class ActivityExecStatusController {/*** 活动状态变更* 1. 编辑中 -> 提审、关闭* 2. 审核通过 -> 拒绝、关闭、活动中* 3. 审核拒绝 -> 撤审、关闭* 4. 活动中 -> 关闭* 5. 活动关闭 -> 开启* 6. 活动开启 -> 关闭** @param activityId   活动ID* @param beforeStatus 变更前状态* @param afterStatus  变更后状态* @return 返回结果*/public Result execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {// 1. 编辑中 -> 提审、关闭if (Status.Editing.equals(beforeStatus)) {if (Status.Check.equals(afterStatus) || Status.Close.equals(afterStatus)) {ActivityService.execStatus(activityId, beforeStatus, afterStatus);return new Result("0000", "变更状态成功");} else {return new Result("0001", "变更状态拒绝");}}// 2. 审核通过 -> 拒绝、关闭、活动中if (Status.Pass.equals(beforeStatus)) {if (Status.Refuse.equals(afterStatus) || Status.Doing.equals(afterStatus) || Status.Close.equals(afterStatus)) {ActivityService.execStatus(activityId, beforeStatus, afterStatus);return new Result("0000", "变更状态成功");} else {return new Result("0001", "变更状态拒绝");}}// 3. 审核拒绝 -> 撤审、关闭if (Status.Refuse.equals(beforeStatus)) {if (Status.Editing.equals(afterStatus) || Status.Close.equals(afterStatus)) {ActivityService.execStatus(activityId, beforeStatus, afterStatus);return new Result("0000", "变更状态成功");} else {return new Result("0001", "变更状态拒绝");}}// 4. 活动中 -> 关闭if (Status.Doing.equals(beforeStatus)) {if (Status.Close.equals(afterStatus)) {ActivityService.execStatus(activityId, beforeStatus, afterStatus);return new Result("0000", "变更状态成功");} else {return new Result("0001", "变更状态拒绝");}}// 5. 活动关闭 -> 开启if (Status.Close.equals(beforeStatus)) {if (Status.Open.equals(afterStatus)) {ActivityService.execStatus(activityId, beforeStatus, afterStatus);return new Result("0000", "变更状态成功");} else {return new Result("0001", "变更状态拒绝");}}// 6. 活动开启 -> 关闭if (Status.Open.equals(beforeStatus)) {if (Status.Close.equals(afterStatus)) {ActivityService.execStatus(activityId, beforeStatus, afterStatus);return new Result("0000", "变更状态成功");} else {return new Result("0001", "变更状态拒绝");}}return new Result("0001", "非可处理的活动状态变更");}
}
  • 从这个代码实现的结构看,从上到下是一整篇的 if-else
  • 这样的面向过程式开发方式,对于不需要改动代码,也不需要二次迭代的,还是可以使用的(但基本不可能不迭代)。
  • 而且随着状态和需求变化,会越来越难为维护,后面的人也不好看懂并且很容易填充其他的流程进去。

3.3 单元测试

ApiTest.java

package com.lino.design.test;import com.alibaba.fastjson.JSON;
import com.lino.design.ActivityExecStatusController;
import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.Status;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test() {// 初始化数据String activityId = "100001";ActivityService.init(activityId, Status.Editing);ActivityExecStatusController activityExecStatusController = new ActivityExecStatusController();Result resultRefuse = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Refuse);logger.info("测试结果(编辑中To审核拒绝):{}", JSON.toJSONString(resultRefuse));Result resultCheck = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Check);logger.info("测试结果(编辑中To提交审核):{}", JSON.toJSONString(resultCheck));}
}
  • 这个测试主要包括两个功能的验证,一个是从 编辑中审核拒绝 ,另外一个是从 编辑中提交审核
  • 因为从我们的场景流程中可以看到,编辑中 的活动是不能直接到 审核拒绝 ,这中间还需要 提审

测试结果

17:03:34.528 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To审核拒绝){"code":"0001","info":"变更状态拒绝"}
17:03:34.534 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To提交审核){"code":"0000","info":"变更状态成功"}

四、改善代码:状态模式

💡 重构的重点往往是处理掉 if-else ,而想处理掉 if-else 基本离不开接口与抽象类,另外还需要重新改造代码结构。

4.0 引入依赖

<dependencies><dependency><groupId>com.lino</groupId><artifactId>design-20.0-0</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>

4.1 工程结构

design-19.0-2
|——src|——main|--java|--com.lino.design|--impl|		|--CheckState.java|		|--CloseState.java|		|--DoingState.java|		|--EditingState.java|		|--OpenState.java|		|--PassState.java|		|--RefuseState.java|-State.java|-StateHandler.java|--test|--com.lino.design.test|-ApiTest.java

4.2 状态模式结构图

请添加图片描述

  • 以上是状态模式的整个工程结构模型,State 是一个抽象类,定义了各种操作接口(提审、审核、拒审等)。
  • 右侧的不同颜色状态与我们场景模拟中的颜色保持一致,是各种状态流程流转的实现操作。
    • 这里的实现有一个关键点就是每一种状态到下一个状态,都分配到各个实现方法中控制,也就不需要 if 语言进行判断了。
  • 最后是 StateHandler对状态流程的统一处理,里面提供 Map 结构的各项服务接口调用,也就避免了使用 if 判断各项状态转变的流程。

4.3 活动状态变更流程

4.3.1 定义状态抽象类

State.java

package com.lino.design;/*** @description: 活动状态抽象类*/
public abstract class State {/*** 活动提审** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result arraignment(String activityId, Enum<Status> currentStatus);/*** 审核通过** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result checkPass(String activityId, Enum<Status> currentStatus);/*** 审核拒绝** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus);/*** 撤审撤销** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus);/*** 活动关闭** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result close(String activityId, Enum<Status> currentStatus);/*** 活动开启** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result open(String activityId, Enum<Status> currentStatus);/*** 活动执行** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result doing(String activityId, Enum<Status> currentStatus);
}
  • 整个接口中提供了各项状态流转服务的接口,例如:活动提审审核通过审核拒绝撤审撤销活动关闭活动开启 、**活动执行
  • 在这些方法中所有的入参都是一样的,activityId (活动ID)currentStatus (当前状态),只有他们的具体实现是不同的。

4.3.2 待审核状态流转实现

CheckState.java

package com.lino.design.impl;import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;/*** @description: 待审核*/
public class CheckState extends State {@Overridepublic Result arraignment(String activityId, Enum<Status> currentStatus) {return new Result("0001", "待审核状态不可重复提审");}@Overridepublic Result checkPass(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Pass);return new Result("0000", "活动审核通过完成");}@Overridepublic Result checkRefuse(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Refuse);return new Result("0000", "活动审核拒绝完成");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Editing);return new Result("0000", "活动审核撤销回到编辑中");}@Overridepublic Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动审核关闭完成");}@Overridepublic Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "非关闭活动不可开启");}@Overridepublic Result doing(String activityId, Enum<Status> currentStatus) {return new Result("0001", "待审核活动不可执行活动中变更");}
}

4.3.3 活动关闭状态流转实现

CloseState.java

package com.lino.design.impl;import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;/*** @description: 活动关闭*/
public class CloseState extends State {@Overridepublic Result arraignment(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动关闭不可提审");}@Overridepublic Result checkPass(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动关闭不可审核通过");}@Overridepublic Result checkRefuse(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动关闭不可审核拒绝");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动关闭不可撤销审核");}@Overridepublic Result close(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动关闭不可重复关闭");}@Overridepublic Result open(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Open);return new Result("0000", "活动开启完成");}@Overridepublic Result doing(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动关闭不可变更活动中");}
}

4.3.4 活动中状态流转实现

DoingState.java

package com.lino.design.impl;import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;/*** @description: 活动中*/
public class DoingState extends State {@Overridepublic Result arraignment(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动中不可提审");}@Overridepublic Result checkPass(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动中不可审核通过");}@Overridepublic Result checkRefuse(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动中不可审核拒绝");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动中不可撤销审核");}@Overridepublic Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动关闭完成");}@Overridepublic Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动中不可开启");}@Overridepublic Result doing(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动中不可重复执行");}
}

4.3.5 编辑中状态流转实现

EditingState.java

package com.lino.design.impl;import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;/*** @description: 编辑中*/
public class EditingState extends State {@Overridepublic Result arraignment(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Check);return new Result("0000", "活动提审成功");}@Overridepublic Result checkPass(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑中不可审核通过");}@Overridepublic Result checkRefuse(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑中不可审核拒绝");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑中不可撤销审核");}@Overridepublic Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动关闭完成");}@Overridepublic Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "非关闭活动不可开启");}@Overridepublic Result doing(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑活动中不可执行活动中变更");}
}

4.3.6 活动开启状态流转实现

OpenState.java

package com.lino.design.impl;import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;/*** @description: 活动开启*/
public class OpenState extends State {@Overridepublic Result arraignment(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动开启不可提审");}@Overridepublic Result checkPass(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动开启不可审核通过");}@Overridepublic Result checkRefuse(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动开启不可审核拒绝");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动开启不可撤销审核");}@Overridepublic Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动关闭完成");}@Overridepublic Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "活动不可重复开启");}@Overridepublic Result doing(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Doing);return new Result("0000", "活动变更活动中完成");}
}

4.3.7 审核通过状态流转实现

PassState.java

package com.lino.design.impl;import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;/*** @description: 审核通过*/
public class PassState extends State {@Overridepublic Result arraignment(String activityId, Enum<Status> currentStatus) {return new Result("0001", "已审核状态不可重复提审");}@Overridepublic Result checkPass(String activityId, Enum<Status> currentStatus) {return new Result("0001", "已审核状态不可重复审核");}@Overridepublic Result checkRefuse(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Refuse);return new Result("0000", "活动审核拒绝完成");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {return new Result("0001", "审核通过不可撤销(可先拒绝审核)");}@Overridepublic Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动关闭完成");}@Overridepublic Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "非关闭活动不可开启");}@Overridepublic Result doing(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Doing);return new Result("0000", "活动变更活动中完成");}
}

4.3.8 审核拒绝状态流转实现

RefuseState.java

package com.lino.design.impl;import com.lino.design.ActivityService;
import com.lino.design.Result;
import com.lino.design.State;
import com.lino.design.Status;/*** @description: 审核拒绝*/
public class RefuseState extends State {@Overridepublic Result arraignment(String activityId, Enum<Status> currentStatus) {return new Result("0001", "已审核状态不可重复提审");}@Overridepublic Result checkPass(String activityId, Enum<Status> currentStatus) {return new Result("0001", "已审核状态不可重复审核");}@Overridepublic Result checkRefuse(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Refuse);return new Result("0000", "活动审核拒绝不可重复审核");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Editing);return new Result("0000", "撤销审核完成");}@Overridepublic Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动审核关闭完成");}@Overridepublic Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "非关闭活动不可开启");}@Overridepublic Result doing(String activityId, Enum<Status> currentStatus) {return new Result("0001", "审核拒绝不可执行活动为进行中");}
}
  • 这里提供了七个具体实现类。待审核活动关闭活动中编辑中活动开启审核通过审核拒绝
  • 在每个实现类中,每个方法对于不同的类中有不同的实现,也就是不同状态下一步流转操作已经可以在每一个方法中具体控制了。

4.3.9 活动执行者

StateHandler.java

package com.lino.design;import com.lino.design.impl.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @description: 活动执行者*/
public class StateHandler {private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<>();public StateHandler() {// 待审核stateMap.put(Status.Check, new CheckState());// 已关闭stateMap.put(Status.Close, new CloseState());// 活动中stateMap.put(Status.Doing, new DoingState());// 编辑中stateMap.put(Status.Editing, new EditingState());// 已开启stateMap.put(Status.Open, new OpenState());// 审核通过stateMap.put(Status.Pass, new PassState());// 审核拒绝stateMap.put(Status.Refuse, new RefuseState());}/*** 活动提审** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public Result arraignment(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).arraignment(activityId, currentStatus);}/*** 审核通过** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public Result checkPass(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).checkPass(activityId, currentStatus);}/*** 审核拒绝** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public Result checkRefuse(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);}/*** 撤审撤销** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public Result checkRevoke(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);}/*** 活动关闭** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public Result close(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).close(activityId, currentStatus);}/*** 活动开启** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public Result open(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).open(activityId, currentStatus);}/*** 活动执行** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public Result doing(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).doing(activityId, currentStatus);}
}
  • 这是对状态服务的统一控制中心,可以看到子啊构造函数中提供了所有状态和实现的具体关联,放到 Map 数据结构中。
  • 同时提供了不同名称的接口操作类,让外部调用方可以更加容易的使用此项功能接口,而不需要像在之前还得传两个状态来判断。

4.4 单元测试

4.4.1 编辑中到提审活动测试验证

ApiTest.java

@Test
public void test_Editing2Arraignment() {String activityId = "100001";ActivityService.init(activityId, Status.Editing);StateHandler stateHandler = new StateHandler();Result result = stateHandler.arraignment(activityId, Status.Editing);logger.info("测试结果(编辑中To提审活动):{}", JSON.toJSONString(result));logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityStatus(activityId)));
}

测试结果

20:48:10.752 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To提审活动){"code":"0000","info":"活动提审成功"}
20:48:10.788 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860490764,"endTime":1675860490764,"status":"Check"} 状态:"Check"
  • 从编辑中到提审活动到状态流转,测试验证结果显示正常。

4.4.2 编辑中到开启活动测试验证

ApiTest.java

@Test
public void test_Editing2Open() {String activityId = "100001";ActivityService.init(activityId, Status.Editing);StateHandler stateHandler = new StateHandler();Result result = stateHandler.open(activityId, Status.Editing);logger.info("测试结果(编辑中To开启活动):{}", JSON.toJSONString(result));logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityStatus(activityId)));
}

测试结果

20:48:57.997 [main] INFO  com.lino.design.test.ApiTest - 测试结果(编辑中To开启活动){"code":"0001","info":"非关闭活动不可开启"}
20:48:58.041 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860538008,"endTime":1675860538008,"status":"Editing"} 状态:"Editing"
  • 从编辑中到开启活动到状态流转可以看到,测试结果是拒绝- 非关闭活动不可开启
  • 按照流程流转结果,预期结果正常,不能从编辑中直接到活动开启,需要经过审批阶段。

4.4.3 审批拒绝到活动中测试验证

ApiTest.java

@Test
public void test_Editing2Doing() {String activityId = "100001";ActivityService.init(activityId, Status.Refuse);StateHandler stateHandler = new StateHandler();Result result = stateHandler.doing(activityId, Status.Refuse);logger.info("测试结果(拒绝To活动中):{}", JSON.toJSONString(result));logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityStatus(activityId)));
}

测试结果

20:49:51.974 [main] INFO  com.lino.design.test.ApiTest - 测试结果(拒绝To活动中){"code":"0001","info":"审核拒绝不可执行活动为进行中"}
20:49:52.015 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860591979,"endTime":1675860591979,"status":"Refuse"} 状态:"Refuse"
  • 从审批拒绝到活动中到状态流转可以看到,同样是拒绝- 审批拒绝不可执行活动中 为进行中,满足预期。

4.4.4 审批拒绝到撤审测试验证

ApiTest.java

@Test
public void test_Editing2Revoke() {String activityId = "100001";ActivityService.init(activityId, Status.Refuse);StateHandler stateHandler = new StateHandler();Result result = stateHandler.checkRevoke(activityId, Status.Refuse);logger.info("测试结果(拒绝To撤审):{}", JSON.toJSONString(result));logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityStatus(activityId)));
}

测试结果

20:50:39.921 [main] INFO  com.lino.design.test.ApiTest - 测试结果(拒绝To撤审){"code":"0000","info":"撤销审核完成"}
20:50:39.960 [main] INFO  com.lino.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1675860639937,"endTime":1675860639937,"status":"Editing"} 状态:"Editing"
  • 从审批拒绝到撤审到状态流转可以看到,按照状态到流转约定,审批拒绝后可以撤审,测试结果满足预期。

💡 综上,以上四个测试类分别模拟了不同状态之间到有效流转和拒绝流转,不同的状态服务处理不同的服务内容。

五、总结:状态模式

  • 从以上两种方式对一个需求的实现对比可以看到,在使用设计模式处理后,已经没有了 if-else ,代码的结构也更加清晰,易于扩展。
  • 在实现结构的编码方式上,可以看到不再是面向过程的编程,而是面向对象的编程。
  • 状态模式的优点:
    • 状态模式满足了单一职责和开闭原则。
    • 当只有满足这种结构时,才会发现代码的扩展是容易的,也就是增加或修改功能不会影响整体。
  • 状态模式的缺点:
    • 如果状态和各项流转较多,就会产生较多的实现类。因此,可能会给代码的实现增加时间成本。
    • 因为如果遇到这种场景可以按需评估投入回报率,主要在于是否会经常修改,是否可以做成组件化,抽离业务功能与非业务功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/115265.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ShardingSphere——柔性事务SEATA原理

摘要 Apache ShardingSphere集成了 SEATA 作为柔性事务的使用方案&#xff0c;本文主要介绍其实现ShardingSphere中柔性事务SEATA原理原理。帮助你更好的理解ShardingSphere原理。同时帮助大家更好的使用柔性事务SEATA原理。 一、Seata柔性事务 Apache ShardingSphere 集成了…

keil在点击debug无法运行(全速运行)

1、今天发现我之前可以debug的程序&#xff0c;在板子上无法debug了&#xff0c;打断点完全没用 2、换了电脑&#xff0c;带板子过去也这样&#xff0c;之前可以运行的代码都debug不了 3、按照网上的方法&#xff0c;都不行&#xff0c;全速运行&#xff0c;单步执行都是灰色…

跨屏无界 | ZlongGames 携手 Google Play Games 打造无缝游戏体验

一款经典游戏&#xff0c;会在时间的沉淀中被每一代玩家所怀念&#xff0c;经久不衰。对于紫龙游戏来讲&#xff0c;他们就是这样一群怀揣着创作出经典游戏的初心而聚集在一起的团队&#xff0c;致力于研发出被广大玩家喜爱的作品。 从 2015 年团队成立&#xff0c;到 2019 年走…

创作纪念日-我的第1024天

机缘 不知不觉已经成为创作者的第1024天啦… … 刚开始接触博客的初衷就是为了记笔记&#x1f4d2;、记总结&#x1f4dd;&#xff0c;或许对于当时就等同于是为了找工作。坚持学习并持续输出博客一年后&#xff0c;这时我发现再写博客&#xff0c;不在是为了找一份工作&…

大数据平台数据脱敏是什么意思?有哪些方案?

大数据平台包含了海量多样化数据&#xff0c;所以保障大数据平台数据安全非常重要&#xff0c;数据脱敏就是手段之一。今天我们就来简单聊聊大数据平台数据脱敏是什么意思&#xff1f;有哪些方案&#xff1f; 大数据平台数据脱敏是什么意思&#xff1f; 大数据平台数据脱敏简…

Python学习 -- 异常堆栈追踪技术

在编写Python代码时&#xff0c;出现异常是不可避免的。异常堆栈追踪是一种强大的工具&#xff0c;可以帮助我们定位错误发生的位置以及调用栈信息。Python的traceback模块提供了多种方法来获取和展示异常的堆栈信息。本文将详细介绍traceback模块中的print_exc()方法&#xff…

SpingBoot 过滤器和拦截器有什么区别?

1. 运行顺序不同&#xff08;如图&#xff09;&#xff1a; 过滤器是在 Servlet 容器接收到请求之后&#xff0c;但在 Servlet 被调用之前运行的&#xff1b;而拦截器则是在 Servlet 被调用之后&#xff0c;但在响应被发送到客户 端之前运行的。 2. 配置方式不同&#xff1a; …

微信小程序校园生活小助手+后台管理系统|前后分离VUE

《微信小程序校园生活小助手后台管理系统|前后分离VUE》该项目含有源码、文档等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序前台和Java做的后台管理系统&#xff0c;该后台采用前后台前后分离的形式使用JavaVUE 微信小程序——前台涉及技术&#…

WebGIS的一些学习笔记

一、简述计算机网络的Internet 概念、网络类型分类、基本特征和功用是什么 计算机网络的Internet 概念 计算机网络是地理上分散的多台独立自主的计算机遵循约定的通讯协议&#xff0c;通过软、硬件互连以实现交互通信、资源共享、信息交换、协同工作以及在线处理等功能的系统…

数据并行 - DP/DDP/ZeRO

数据并行DP 数据并行的核心思想是&#xff1a;在各个GPU上都拷贝一份完整模型&#xff0c;各自吃一份数据&#xff0c;算一份梯度&#xff0c;最后对梯度进行累加来更新整体模型。理念不复杂&#xff0c;但到了大模型场景&#xff0c;巨大的存储和GPU间的通讯量&#xff0c;就…

vue3渲染函数h的简单使用——定义局部组件

vue3渲染函数h的简单使用 基本用法 创建 Vnodes Vue 提供了一个 h() 函数用于创建 vnodes&#xff1a; import { h } from vueconst vnode h(div, // type{ id: foo, class: bar }, // props[/* children */] )更多用法 详情查看官方文档 在SFC中定义局部组件使用 h函数…

浅谈 Pytest+HttpRunner 如何展开接口测试!

软件测试有多种多样的方法和技术&#xff0c;可以从不同角度对它们进行分类。其中&#xff0c;根据软件生命周期&#xff0c;针对不同的测试对象与目标&#xff0c;可将测试过程分为 4 个阶段&#xff1a;单元测试、集成测试、系统测试和验收测试。本文着重介绍了如何借用 pyte…

QT day3

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }void Widget::on_ziti_clicked()//字體按鈕的槽函數 {//直接調用Qfontdialo…

堆的基本存储(Java 实例代码)

堆的基本存储 一、概念及其介绍 堆(Heap)是计算机科学中一类特殊的数据结构的统称。 堆通常是一个可以被看做一棵完全二叉树的数组对象。 堆满足下列性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值。堆总是一棵完全二叉树。 二、适用说明 堆是利用完…

Java“牵手”天猫商品快递费用API接口数据,天猫API接口申请指南

天猫平台商品快递费用接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取天猫商品的标题、价格、库存、商品快递费用&#xff0c;宝贝ID&#xff0c;发货地&#xff0c;区域ID&#xff0c;快递费用&#xff0c;月销量、总销量、库存、详情…

手把手教你写出第一个C语言程序

Hello, World! 1. 前言2. 准备知识2.1 环境2.2 文件的分类2.3 注释2.3.1 注释的作用2.3.2 注释的两种风格2.3.2.1 C语言的注释风格2.3.2.2 C的注释风格 2.3.3 VS中注释和取消注释的快捷键 3. 开始演示3.1 创建项目3.2 创建源文件3.3 写代码3.4 编译链接运行 4. 代码解释4.1 写主…

电脑组装教程分享!

案例&#xff1a;如何自己组装电脑&#xff1f; 【看到身边的小伙伴组装一台自己的电脑&#xff0c;我也想试试。但是我对电脑并不是很熟悉&#xff0c;不太了解具体的电脑组装步骤&#xff0c;求一份详细的教程&#xff01;】 电脑已经成为我们日常生活中不可或缺的一部分&a…

如何在VR头显端实现低延迟的RTSP或RTMP播放

技术背景 VR&#xff08;虚拟现实技术&#xff09;给我们带来身临其境的视觉体验&#xff0c;广泛的应用于城市规划、教育培训、工业仿真、房地产、水利电力、室内设计、文旅、军事等众多领域&#xff0c;常用的行业比如&#xff1a; 教育行业&#xff1a;VR头显可以用于教育…

自动化管理管理工具----Ansible

目录 ​编辑 一、Ansible概念 1.1特点 二、工作机制&#xff08;日常模块&#xff09; 2.1 核心程序 三、Ansible 环境安装部署 四、ansible 命令行模块 4.1command 模块 4.2shell 模块 4.3cron 模块 4.4user 模块 4.5group 模块 4.6copy模块 4.7file模块 4.8ho…

记录--vue 拉伸指令

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 在我们项目开发中,经常会有布局拉伸的需求,接下来 让我们一步步用 vue指令 实现这个需求 动手开发 在线体验 codesandbox.io/s/dawn-cdn-… 常规使用 解决拉伸触发时机 既然我们使用了指令的方式…