当前位置: 首页 > news >正文

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

相关文章:

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

文章目录 一、定义&#xff1a;状态模式二、模拟场景&#xff1a;状态模式2.1 状态模式2.2 引入依赖2.3 工程结构2.4 模拟审核状态流转2.4.1 活动状态枚举2.4.2 活动信息类2.4.3 活动服务接口2.4.4 返回结果类 三、违背方案&#xff1a;状态模式3.0 引入依赖3.1 工程结构3.2 活…...

很多应用都是nginx+apache+tomcat

nginx 负责负载均衡&#xff0c;将大量的访问量平衡分配给多个服务器 apache 是用来处理静态html、图片等资源&#xff0c;在对HTML解析、响应等方面比tomcat效率更高。 tomcat 处理JSP等内容&#xff0c;进行后台业务操作。 upstream bbb.com.cn{ server 192.168.10.1:80 ;…...

原型模式:复制对象的技巧

欢迎来到设计模式系列的第六篇文章&#xff01;在前面的几篇文章中&#xff0c;我们已经学习了一些常见的设计模式&#xff0c;今天我们将继续探讨另一个重要的设计模式——原型模式。 原型模式简介 原型模式是一种创建型设计模式&#xff0c;它主要用于复制对象。原型模式通…...

ClickHouse进阶(五):副本与分片-1-副本与分片

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术,IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &#x1f4cc;订阅…...

Android 华为手机荣耀8X调用系统裁剪工具不能裁剪方形图片,裁剪后程序就奔溃,裁剪后获取不到bitmap的问题

买了个华为荣耀8X,安装自己写的App后,调用系统裁剪工具发现裁剪是圆形的,解决办法: //专门针对华为手机解决华为手机裁剪图片是圆形图片的问题 if (Build.MANUFACTURER.equals("HUAWEI")) {intent.putExtra("aspectX", 9998);intent.putExtra("a…...

《Flink学习笔记》——第十二章 Flink CEP

12.1 基本概念 12.1.1 CEP是什么 1.什么是CEP&#xff1f; 答&#xff1a;所谓 CEP&#xff0c;其实就是“复杂事件处理&#xff08;Complex Event Processing&#xff09;”的缩写&#xff1b;而 Flink CEP&#xff0c;就是 Flink 实现的一个用于复杂事件处理的库&#xff08…...

谷歌IndexedDB客户端存储数据

IndexedDB 具有以下主要特点&#xff1a; 1.存储大量数据&#xff1a;IndexedDB 可以存储大量的数据&#xff0c;比如存储离线应用程序的本地缓存或存储在线应用程序的大量数据。 2.结构化数据&#xff1a;IndexedDB 使用对象存储空间&#xff08;Object Stores&#xff09;来…...

天气数据的宝库:解锁天气预报API的无限可能性

前言 天气预报一直是我们日常生活中的重要组成部分。我们依赖天气预报来决定穿什么衣服、何时出行、规划户外活动以及做出关于农业、交通和能源管理等方面的重要决策。然而&#xff0c;要提供准确的天气预报&#xff0c;需要庞大的数据集和复杂的计算模型。这就是天气预报API的…...

插入排序(Insertion Sort)

C自学精简教程 目录(必读) 插入排序 每次选择未排序子数组中的第一个元素&#xff0c;从后往前&#xff0c;插入放到已排序子数组中&#xff0c;保持子数组有序。 打扑克牌&#xff0c;起牌。 输入数据 42 20 17 13 28 14 23 15 执行过程 完整代码 #include <iostream…...

2023蓝帽杯初赛

最近打完蓝帽杯 现在进行复盘 re 签到题 直接查看源代码 输出的内容就是 变量s 变量 number 而这都是已经设定好了的 所以flag就出来了 WhatisYourStory34982733 取证 案件介绍 取证案情介绍&#xff1a; 2021年5月&#xff0c;公安机关侦破了一起投资理财诈骗类案件&a…...

风险评估

风险评估概念 风险评估是一种系统性的方法&#xff0c;用于识别、评估和量化潜在的风险和威胁&#xff0c;以便组织或个人能够采取适当的措施来管理和减轻这些风险。 风险评估的目的 风险评估要素关系 技术评估和管理评估 风险评估分析原理 风险评估服务 风险评估实施流程...

直播软件app开发中的AI应用及前景展望

在当今数字化时代&#xff0c;直播市场蓬勃发展&#xff0c;而直播软件App成为人们获取实时信息和娱乐的重要渠道。随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;直播软件App开发正逐渐融入AI的应用&#xff0c;为用户带来更智能、更个性化的直播体验。 …...

vscode html使用less和快速获取标签less结构

扩展插件里面搜索 css tree 插件 下载 使用方法 选择你要生成的标签结构然后按CTRLshiftp 第一次需要在输入框输入 get 然后选择 Generate CSS tree less结构就出现在这个里面直接复制到自己的less文件里面就可以使用了 在html里面使用less 下载 Easy LESS 插件 自己创建…...

excel中的引用与查找函数篇1

1、COLUMN(reference)&#xff1a;返回与列号对应的数字 2、ROW(reference)&#xff1a;返回与行号对应的数字 参数reference表示引用/参考单元格&#xff0c;输入后引用单元格后colimn()和row()会返回这个单元格对应的列号和行号。若参数reference没有引用单元格&#xff0c;…...

【python】—— 函数详解

前言&#xff1a; 本期&#xff0c;我们将要讲解的是有关python中函数的相关知识&#xff01;&#xff01;&#xff01; 目录 &#xff08;一&#xff09;函数是什么 &#xff08;二&#xff09;语法格式 &#xff08;三&#xff09;函数参数 &#xff08;四&#xff09;函…...

springboot web开发登录拦截器

在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。 应用场景 日志记录&#xff0c;可以记录请求信息的日志&#xff0c;以便进行信息监控、信息统计等。权限检查&#xff1a;如登陆检测&#xff…...

大数据课程K17——Spark的协同过滤法

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的协同过滤概念; 一、协同过滤概念 1. 概念 协同过滤是一种借助众包智慧的途径。它利用大量已有的用户偏好来估计用户对其未接触过的物品的喜好程度。其内在思想是相似度的定义…...

【力扣】1588. 所有奇数长度子数组的和 <前缀和>

【力扣】1588. 所有奇数长度子数组的和 给你一个正整数数组 arr &#xff0c;请你计算所有可能的奇数长度子数组的和。子数组 定义为原数组中的一个连续子序列。请你返回 arr 中 所有奇数长度子数组的和 。 示例 1&#xff1a; 输入&#xff1a;arr [1,4,2,5,3] 输出&#x…...

socket,tcp,http三者之间的原理和区别

目录 1、TCP/IP连接 2、HTTP连接 3、SOCKET原理 4、SOCKET连接与TCP/IP连接 5、Socket连接与HTTP连接 socket&#xff0c;tcp&#xff0c;http三者之间的区别和原理 http、TCP/IP协议与socket之间的区别 下面的图表试图显示不同的TCP/IP和其他的协议在最初OSI模型中的位置…...

【FPGA零基础学习之旅#11】数码管动态扫描

&#x1f389;欢迎来到FPGA专栏~数码管动态扫描 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能指正…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...