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.hqwc.cn/news/97268.html

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

相关文章

stable diffusion实践操作-批次出图

系列文章目录 stable diffusion实践操作 文章目录 系列文章目录前言一、批次出图介绍1.1 webUI设置1.2 参数介绍 二、批次出图使用2.1 如何设置2.1 效果展示 总结 前言 本章主要介绍SD批次出图。 想要一次产生多张图片的时候使用。 一、批次出图介绍 1.1 webUI设置 1.2 参数…

【写作秘书】emoji

&#x1f4cc;无用的知识 | 写作美秘 emoji markdown能写的表情有限&#xff0c;主要有时候我记不住一些词汇&#x1f440;:eyes:&#x1f448;这种猫猫狗狗一个单词的简单. 分享一个我常用的emoji网站 https://emojipedia.org/ 有时候文章码太长&#xff0c;除了字体颜色换换…

Leetcode:349. 两个数组的交集【题解超详细】

题目 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 难度&#xff1a;简单 题目链接&#xff1a;349.两个数组的交集 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,2,1], nums2 [2,…

突破销售瓶颈:亚马逊卖家如何借力TikTok网红营销?

随着社交媒体的崛起&#xff0c;营销方式也在不断变革。TikTok作为一款风靡全球的短视频平台&#xff0c;吸引了数以亿计的用户&#xff0c;成为了品牌宣传和销售的新热点。对于亚马逊卖家而言&#xff0c;通过合理运用TikTok网红营销策略&#xff0c;可以有效提升产品的曝光度…

在本机搭建自己的ftp服务器--最简单的方法(详细教程)

目录 前言 具体步骤 总结 加油各位( •̀ ω •́ )y 期待与君再相逢 前言 FTP服务器可以在局域网中快速传输文件&#xff0c;是在互联网上提供文件存储和访问服务的计算机&#xff0c;它们依照FTP协议提供服务。 FTP是File Transfer Protocol(文件传输协议)。顾名思义&#x…

【原创】鲲鹏ARM构架openEuler操作系统安装Oracle 19c

作者:einyboy 【原创】鲲鹏ARM构架openEuler操作系统安装Oracle 19c | 云非云计算机科学、自然科学技术科谱http://www.nclound.com/index.php/2023/09/03/%E3%80%90%E5%8E%9F%E5%88%9B%E3%80%91%E9%B2%B2%E9%B9%8Farm%E6%9E%84%E6%9E%B6openeuler%E6%93%8D%E4%BD%9C%E7%B3%BB%…

虹科产线实时数采检测方案——高速采集助力智能化升级

01 产线数采检测相关技术背景 1.1 典型场景 对于产线数采检测&#xff0c;让我们从典型的工厂场景开始介绍。 每个工位都有上位机监控下方的PLC控制器。指令、执行单元和作用对象的状态通过内置传感器进行采集和测量&#xff0c;反馈给PLC实现闭环控制。 工业4.0和智能制…

传输层—UDP原理详解

目录 前言 1.netstat 2.pidof 3.UDP协议格式 4.UDP的特点 5.面向数据报 6.UDP的缓冲区 7.UDP使用注意事项 8.基于UDP的应用层协议 总结 前言 在之前的文章中为大家介绍了关于网络协议栈第一层就是应用层&#xff0c;包含套接字的使用&#xff0c;在应用层编码实现服务…

【LeetCode】1654:到家的最少跳跃次数的解题思路 关于力扣无法return的BUG的讨论

文章目录 一、题目二、题解与代码三、神奇的BUG3.1 无法执行的 return 和 break 语句3.2 通过另一个 break 解决 一、题目 有一只跳蚤的家在数轴上的位置 x 处。请你帮助它从位置 0 出发&#xff0c;到达它的家。 跳蚤跳跃的规则如下&#xff1a; 它可以 往前 跳恰好 a 个位…

记一次批量更新mysql数据过程

一、前言 需求背景&#xff1a;mysql数据库中有一个表的数据&#xff08;600多万&#xff09;有一个字段的内容需要解密再通过另外一种加密方式进行加密再回存。通过java程序计算完成更新。 二、方案一 一条条计算更新。这里是将手机号解密&#xff0c;在通过另外一种方式回…

Zookeeper 入门

第 1 章 Zookeeper 入门 1.1概述 Zookeeper从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的数据&#xff0c;然后接受观察者的注册&#xff0c;一旦这些数据的状态发生变化&#xff0c;Zookeeper就将…

MySQL之事务与引擎

目录 一、事物 1、事务的概念 2、事务的ACID特点 3、事务之间的相互影响 4、Mysql及事务隔离级别(四种) 1、查询会话事务隔离级别 2、查询会话事务隔离级别 3、设置全局事务隔离级别 4、设置会话事务隔离级别 5、事务控制语句 6、演示 1、测试提交事务 2、测试事务回滚 4…