状态模式
# 定义
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
>状态模式描述的是一个行为下的多种状态变更,比如我们最常见的一个网站的页面,在你登录与不登录下展示的内容是略有差异的(不登录不能展示个人信息),而这种登录与不登录就是我们通过改变状态,而让整个行为发生了变化。
优点:
1. 封装了转换规则。
2. 枚举可能的状态,在枚举状态之前需要确定状态种类。
3. 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4. 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5. 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点:
1. 状态模式的使用必然会增加系统类和对象的个数。
2. 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3. 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景:
1. 行为随状态改变而改变的场景。
2. 条件、分支语句的代替者。
# 实践
本次模拟营销活动审核状态流转场景(一个活动的上线是多个层级审核上线的)

>在上图中也可以看到我们的流程节点中包括了各个状态到下一个状态扭转的关联条件,比如;审核通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是我们要完成的场景处理。
>
>大部分程序员基本都开发过类似的业务场景,需要对活动或者一些配置需要审核后才能对外发布,而这个审核的过程往往会随着系统的重要程度而设立多级控制,来保证一个活动可以安全上线,避免造成资损。
代码结构:

状态枚举(Status)
```java
public enum Status {
// 1创建编辑、2待审核、3审核通过(任务扫描成活动中)、4审核拒绝(可以撤审到编辑状态)、5活动中、6活动关闭、7活动开启(任务扫描成活动中)
Editing, Check, Pass, Refuse, Doing, Close, Open
}
```
活动对象(ActivityInfo)
```java
@Data
public class ActivityInfo {
private String activityId; // 活动ID
private String activityName; // 活动名称
private Enum<Status> status; // 活动状态
private Date beginTime; // 开始时间
private Date endTime; // 结束时间
}
```
活动服务(ActivityService)
```java
public class ActivityService {
private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<String, Enum<Status>>();
public static void init(String activityId, Enum<Status> status) {
// 模拟查询活动信息
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.setActivityId(activityId);
activityInfo.setActivityName("早起学习打卡领奖活动");
activityInfo.setStatus(status);
activityInfo.setBeginTime(new Date());
activityInfo.setEndTime(new Date());
statusMap.put(activityId, status);
}
/**
* 查询活动信息
*
* @param activityId 活动ID
* @return 查询结果
*/
public static ActivityInfo queryActivityInfo(String activityId) {
// 模拟查询活动信息
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.setActivityId(activityId);
activityInfo.setActivityName("早起学习打卡领奖活动");
activityInfo.setStatus(statusMap.get(activityId));
activityInfo.setBeginTime(new Date());
activityInfo.setEndTime(new Date());
return activityInfo;
}
/**
* 查询活动状态
*
* @param activityId 活动ID
* @return 查询结果
*/
public static Enum<Status> queryActivityStatus(String activityId) {
return statusMap.get(activityId);
}
/**
* 执行状态变更
*
* @param activityId 活动ID
* @param beforeStatus 变更前状态
* @param afterStatus 变更后状态 b
*/
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 中获取。
```java
@Data
public class Result {
private String code; // 编码
private String info; // 描述
public Result(String code, String info) {
this.code = code;
this.info = info;
}
}
```
定义状态抽象类
```java
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);
}
```
各种状态流转实现
```java
public class CheckState extends State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "待审核状态不可重复提审");
}
@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Pass);
return new Result("0000", "活动审核通过完成");
}
@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
return new Result("0000", "活动审核拒绝完成");
}
@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Editing);
return new Result("0000", "活动审核撤销回到编辑中");
}
@Override
public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动审核关闭完成");
}
@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}
@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "待审核活动不可执行活动中变更");
}
}
```
```java
public class CloseState extends State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可提审");
}
@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可审核通过");
}
@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可审核拒绝");
}
@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可撤销审核");
}
@Override
public Result close(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可重复关闭");
}
@Override
public Result open(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Open);
return new Result("0000", "活动开启完成");
}
@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动关闭不可变更活动中");
}
}
```
```java
public class DoingState extends State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可提审");
}
@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可审核通过");
}
@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可审核拒绝");
}
@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可撤销审核");
}
@Override
public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动关闭成功");
}
@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可开启");
}
@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动中不可重复执行");
}
}
```
```java
public class EditingState extends State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Check);
return new Result("0000", "活动提审成功");
}
@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中不可审核通过");
}
@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中不可审核拒绝");
}
@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中不可撤销审核");
}
@Override
public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动关闭成功");
}
@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}
@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "编辑中活动不可执行活动中变更");
}
}
```
```java
public class OpenState extends State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可提审");
}
@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可审核通过");
}
@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可审核拒绝");
}
@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动开启不可撤销审核");
}
@Override
public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动关闭完成");
}
@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "活动不可重复开启");
}
@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Doing);
return new Result("0000", "活动变更活动中完成");
}
}
```
```java
public class PassState extends State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复提审");
}
@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复审核");
}
@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
return new Result("0000", "活动审核拒绝完成");
}
@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "审核通过不可撤销(可先拒绝审核)");
}
@Override
public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动审核关闭完成");
}
@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}
@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Doing);
return new Result("0000", "活动变更活动中完成");
}
}
```
```java
public class RefuseState extends State {
@Override
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复提审");
}
@Override
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "已审核状态不可重复审核");
}
@Override
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
return new Result("0000", "活动审核拒绝不可重复审核");
}
@Override
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Editing);
return new Result("0000", "撤销审核完成");
}
@Override
public Result close(String activityId, Enum<Status> currentStatus) {
ActivityService.execStatus(activityId, currentStatus, Status.Close);
return new Result("0000", "活动审核关闭完成");
}
@Override
public Result open(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "非关闭活动不可开启");
}
@Override
public Result doing(String activityId, Enum<Status> currentStatus) {
return new Result("0001", "审核拒绝不可执行活动为进行中");
}
}
```
状态处理服务
```java
public class StateHandler {
private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<Enum<Status>, State>();
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()); // 审核拒绝
}
public Result arraignment(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).arraignment(activityId, currentStatus);
}
public Result checkPass(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).checkPass(activityId, currentStatus);
}
public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);
}
public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);
}
public Result close(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).close(activityId, currentStatus);
}
public Result open(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).open(activityId, currentStatus);
}
public Result doing(String activityId, Enum<Status> currentStatus) {
return stateMap.get(currentStatus).doing(activityId, currentStatus);
}
}
```
测试类
```java
@Slf4j
public class ApiTest {
@Test
public void test_Editing2Arraignment() {
String activityId = "100001";
ActivityService.init(activityId, Status.Editing);
StateHandler stateHandler = new StateHandler();
Result result = stateHandler.arraignment(activityId, Status.Editing);
log.info("测试结果(编辑中To提审活动):{}", JSON.toJSONString(result));
log.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
@Test
public void test_Editing2Open() {
String activityId = "100001";
ActivityService.init(activityId, Status.Editing);
StateHandler stateHandler = new StateHandler();
Result result = stateHandler.open(activityId, Status.Editing);
log.info("测试结果(编辑中To开启活动):{}", JSON.toJSONString(result));
log.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
@Test
public void test_Refuse2Doing() {
String activityId = "100001";
ActivityService.init(activityId, Status.Refuse);
StateHandler stateHandler = new StateHandler();
Result result = stateHandler.doing(activityId, Status.Refuse);
log.info("测试结果(拒绝To活动中):{}", JSON.toJSONString(result));
log.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
@Test
public void test_Refuse2Revoke() {
String activityId = "100001";
ActivityService.init(activityId, Status.Refuse);
StateHandler stateHandler = new StateHandler();
Result result = stateHandler.checkRevoke(activityId, Status.Refuse);
log.info("测试结果(拒绝To撤审):{}", JSON.toJSONString(result));
log.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
}
```
测试结果
```
15:54:41.931 [main] INFO ApiTest - 测试结果(拒绝To活动中):{"code":"0001","info":"审核拒绝不可执行活动为进行中"}
15:54:41.938 [main] INFO ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1645602881933,"endTime":1645602881933,"status":"Refuse"} 状态:"Refuse"
Process finished with exit code 0
```
# 总结
- 从以上的两种方式对一个需求的实现中可以看到,在第二种使用设计模式处理后已经没有了 if...else,代码的结构也更加清晰易于扩展。这就是设计模式的好处,可以非常强大的改变原有代码的结构,让以后的扩展和维护都变得容易些。
- 在实现结构的编码方式上可以看到这不再是面向过程的编程,而是面向对象的结构。并且这样的设计模式满足了单一职责和开闭原则,当你只有满足这样的结构下才会发现代码的扩展是容易的,也就是增加和修改功能不会影响整体的变化。
- 但如果状态和各项流转较多像本文的案例中,就会产生较多的实现类。因此可能也会让代码的实现上带来了时间成本,因为如果遇到这样的场景可以按需评估投入回报率。主要点在于看是否经常修改、是否可以做成组件化、抽离业务与非业务功能。
>原文链接:https://github.com/fuzhengwei/CodeGuide/blob/master/docs/md/develop/design-pattern/2020-07-02-%E9%87%8D%E5%AD%A6%20Java%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8A%E5%AE%9E%E6%88%98%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F%E3%80%8B.md

【设计模式】状态模式