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;}
}
三、违背方案:状态模式
对于这样各种状态的变更,最让我们直接想到的就是使用
if
和else
进行判断处理。
每一个状态可以流转到下一个什么状态,都可以使用嵌套的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:状态模式
文章目录 一、定义:状态模式二、模拟场景:状态模式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 活…...

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

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

ClickHouse进阶(五):副本与分片-1-副本与分片
进入正文前,感谢宝子们订阅专题、点赞、评论、收藏!关注IT贫道,获取高质量博客内容! 🏡个人主页:含各种IT体系技术,IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 📌订阅…...

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? 答:所谓 CEP,其实就是“复杂事件处理(Complex Event Processing)”的缩写;而 Flink CEP,就是 Flink 实现的一个用于复杂事件处理的库(…...

谷歌IndexedDB客户端存储数据
IndexedDB 具有以下主要特点: 1.存储大量数据:IndexedDB 可以存储大量的数据,比如存储离线应用程序的本地缓存或存储在线应用程序的大量数据。 2.结构化数据:IndexedDB 使用对象存储空间(Object Stores)来…...

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

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

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

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

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

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

excel中的引用与查找函数篇1
1、COLUMN(reference):返回与列号对应的数字 2、ROW(reference):返回与行号对应的数字 参数reference表示引用/参考单元格,输入后引用单元格后colimn()和row()会返回这个单元格对应的列号和行号。若参数reference没有引用单元格,…...

【python】—— 函数详解
前言: 本期,我们将要讲解的是有关python中函数的相关知识!!! 目录 (一)函数是什么 (二)语法格式 (三)函数参数 (四)函…...

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

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

【力扣】1588. 所有奇数长度子数组的和 <前缀和>
【力扣】1588. 所有奇数长度子数组的和 给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。子数组 定义为原数组中的一个连续子序列。请你返回 arr 中 所有奇数长度子数组的和 。 示例 1: 输入: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,tcp,http三者之间的区别和原理 http、TCP/IP协议与socket之间的区别 下面的图表试图显示不同的TCP/IP和其他的协议在最初OSI模型中的位置…...

【FPGA零基础学习之旅#11】数码管动态扫描
🎉欢迎来到FPGA专栏~数码管动态扫描 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹 ✨博客主页:小夏与酒的博客 🎈该系列文章专栏:FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误,希望大家能指正…...

JavaExcel:自动生成数据表并插入数据
故事背景 出于好奇,当下扫描excel读取数据进数据库 or 导出数据库数据组成excel的功能层出不穷,代码也是前篇一律,poi或者easy excel两种SDK的二次利用带来了各种封装方法。 那么为何不能直接扫描excel后根据列的属性名与行数据的属性建立S…...

哪吒汽车“三头六臂”之「浩智电驱」
撰文 / 翟悦 编审 / 吴晰 8月21日,在哪吒汽车科技日上,哪吒汽车发布“浩智战略2025”以及浩智技术品牌2.0。根据公开信息,主编梳理了以下几点:◎浩智滑板底盘支持400V/800V双平台◎浩智电驱包括180kW 400V电驱系统和250kW 800…...

补码的反码加1为什么是原码?
搞了半个小时,终于弄懂了。 168421原码10011反码01100补码01101 学到这里了,我们肯定知道,原码补码 0,在这里也就是 19 13 32,溢出来的一位正好舍去了; 所以说,对啊,只要保证…...

光刻机是怎么做出来的
文章目录 一、光刻机的基本原理二、光刻机的制造过程三、光刻机的制造要求四、光刻机的发展趋势 光刻机是半导体工艺制造中非常重要的设备之一,它是用来制作微细结构的关键工具之一。相信大家都知道,半导体工艺中最小的制造单位是晶体管,而制…...

CocosCreator3.8研究笔记(二)windows环境 VS Code 编辑器的配置
一、设置文件显示和搜索过滤步骤 为了提高搜索效率以及文件列表中隐藏不需要显示的文件, VS Code 需要设置排除目录用于过滤。 比如 cocoscreator 中,编辑器运行时会自动生成一些目录:build、temp、library, 所以应该在搜索中排除…...

Rust--流程控制
循环/判断 ref: 流程控制 - Rust语言圣经(Rust Course) 判断 if condition true {// A... } else {// B... }if 语句块是表达式,所以可以为变量赋值,当然要注意的是保证返回的类型相同: fn main() {let condition true;let number if c…...

mate60的麒麟9000s和麒麟9000是一款CPU吗
答案:不是 论证: 1.在核心方便9000是1个高频A77,3个低频A77,4个A55组成的。9000S是2个高频A34核心,6个定制A78AE核心和4个A510核心并搭载超线程技术(详见新华网新华网地址) 2.GPU截然不同&am…...

查漏补缺 - JS三 WebAPI
目录 BOMhistory DOM操作DOM1,dom.children 和 dom.childNodes 区别2,dom.remove()3,其他常用 API DOM 属性1,标准属性2,自定义属性 DOM 内容DOM样式DOM事件 JavaScript 包括 EcmaScript 和 WebAPI EcmaScript 包括 语…...

如何熟练使用vector?
🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏1: 🍔🍟🌯C语言初阶 🐻推荐专栏2: 🍔🍟🌯C语言进阶 🔑个人信条: 🌵知行合一 …...

gitlab-rake gitlab:backup:create 执行报错 Errno::ENOSPC: No space left on device
gitlab仓库备份执行 gitlab-rake gitlab:backup:create报错如下: 问题分析:存储备份的空间满 解决方法: 方法1:清理存放路径,删除不需要文件,释放空间。 方法2:创建一个根目录的挂载点&#x…...