状态模式介绍
状态模式描述的是一个行为下的多种状态变更。例如网站的页面,在登录与没有登录的状态下我们看见的内容有时候是不一样的,如果不登录就不能展示某些页面。
活动审批状态转换场景
比如一个流程是由多个层级审批上线的

可以看到流程节点包括了各个状态到下一个状态的关联条件,比如审批通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是要完成的场景。
很多程序员都开发过类似的业务,需要对活动或者一些配置进行审批才能对外发布,而审批的过程往往会随着系统的额重要程度的提高而设立多级控制,以保证一个活动可以安全上线,避免造成损失。当然,有时会用一些审批流的过程配置,也非常便于开发类似的流程,可以在配置中设定某个节点的审批人员。
场景模拟工程
基本活动信息
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | @Data@AllArgsConstructor
 @NoArgsConstructor
 public class ActivityInfo {
 private String activityId;
 private String activityName;
 private Date beginTime;
 private Date endTime;
 private Enum<Status> status;
 }
 
 | 
活动枚举状态
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | package com.bestrookie;public enum Status {
 
 Editing,
 Check,
 Pass,
 Refuse,
 Doing,
 Close,
 Open
 }
 
 | 
这里是活动的枚举:1、创建编辑 2、待审批 3、审批通过 4、审批拒绝 5、活动中 6、活动关闭 7、活动开启
活动服务接口
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 
 | package com.bestrookie;import com.bestrookie.pojo.ActivityInfo;
 import java.util.Date;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 
 
 
 
 public class ActivityService {
 private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<>();
 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);
 }
 
 
 
 
 
 public static ActivityInfo queryActivityInfo(String activityId){
 ActivityInfo activityInfo = new ActivityInfo();
 activityInfo.setActivityId(activityId);
 activityInfo.setActivityName("早起学习啊");
 activityInfo.setStatus(statusMap.get(activityId));
 activityInfo.setEndTime(new Date());
 activityInfo.setBeginTime(new Date());
 return activityInfo;
 }
 
 
 
 
 
 public static  Enum<Status> queryActivityStatus(String activityId){
 return statusMap.get(activityId);
 }
 
 
 
 
 
 
 public static synchronized void execStatus(String activityId,Enum<Status> beforeStatus,Enum<Status> afterStatus){
 if (!beforeStatus.equals(statusMap.get(activityId))){
 return;
 }
 statusMap.put(activityId,afterStatus);
 }
 }
 
 | 
这个静态类提供了活动的查询和状态变更接口 queryActivtiyInfo、queryActivtyStatus和execStatus。通过使用Map结构记录活动ID和状态变化信息,另外init方法初始活动数据。在实际的开发中,这类信息基本都是从数据库或Redis中获取。
违背设计模式实现
对于各种状态变更的情况,最直接的方式是使用if和else判断。每一个状态可以流转到下一个状态,使用嵌套的if语句实现。
代码实现
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 
 | package com.bestrookie;import com.bestrookie.pojo.ActivityInfo;
 import com.bestrookie.util.Result;
 
 
 
 
 public class ActivityExecStatusController {
 
 
 
 
 
 
 
 
 
 
 
 
 
 public Result execStatus(String activityId,Enum<Status> beforeStatus, Enum<Status> afterStatus){
 
 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","变更状态失败");
 }
 }
 
 if (Status.Pass.equals(beforeStatus)){
 if (Status.Refuse.equals(afterStatus) || Status.Close.equals(afterStatus) || Status.Doing.equals(afterStatus)){
 ActivityService.execStatus(activityId,beforeStatus,afterStatus);
 return new Result("0000","变更状态成功");
 }else {
 return new Result("0001","变更状态失败");
 }
 }
 
 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","变更状态失败");
 }
 }
 
 if (Status.Doing.equals(beforeStatus)){
 if (Status.Close.equals(afterStatus)){
 ActivityService.execStatus(activityId,beforeStatus,afterStatus);
 return new Result("0000","变更状态成功");
 }else {
 return new Result("0001","变更状态失败");
 }
 }
 
 if (Status.Close.equals(beforeStatus)){
 if (Status.Open.equals(afterStatus)){
 ActivityService.execStatus(activityId, beforeStatus, afterStatus);
 return new Result("0000","变更状态成功");
 }else {
 return new Result("0001","变更状态失败");
 }
 }
 
 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("0002","非可处理的活动状态变更");
 }
 }
 
 | 
从代码实现的结构即可,从上到下用了很多的if…else。对于不需要改动也不需要二次迭代的代码,这种面向过程式的开发方式还是可以使用的。但在真实场景中,基本不可能不迭代,随着状态和需求的变化,会越来越难以维护,后来的同时也不容易看懂,很容易将其他的流程填充进去。
状态模式重构代码
重构的重点往往是处理if…else,这离不开接口与抽象类,另外还需要重新改造代码结构。

其中,State是一个抽象类,定义了各种操作接口,包括提审、审批和拒审等。右侧的不同状态与前面的各种状态保持一致,是各种状态流程保持一致,是各种状态流程流转的实现操作。这里有一个关键点,对于每一种状态到下一个状态,都设置为在各个实现方法中控制,不需要if语句判断。最后,StateHandler对状态流程统一处理,里面提供Map结构的各项接口调用,避免使用if语句判断状态转变的流程。
定义抽象类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 
 | package com.bestrookie.design;import com.bestrookie.Status;
 import com.bestrookie.util.Result;
 public abstract class State {
 
 
 
 
 
 
 public abstract Result arraignment(String activityId, Enum<Status> currentStatus);
 
 
 
 
 
 
 
 public abstract Result checkPass(String activityId,Enum<Status> currenStatus);
 
 
 
 
 
 
 
 public abstract Result checkRefuse(String activityId,Enum<Status> currenStatus);
 
 
 
 
 
 
 
 public abstract Result checkRevoke(String activityId,Enum<Status> currentStatus);
 
 
 
 
 
 
 
 public abstract Result close(String activityId,Enum<Status> currentStatus);
 
 
 
 
 
 
 
 public abstract Result open(String activityId,Enum<Status> currentStatus);
 
 
 
 
 
 
 
 public abstract Result doing(String activityId,Enum<Status> currentStatus);
 }
 
 | 
这个接口提供了各项状态流转服务的借口,例如活动提审、审批通过、审批拒绝和撤审撤销等七个方法。在这些方法中,所有的入参都是一样的,即activityId、currentStatus,只有各自的具体实现方式不同的。
部分状态流转实现
编辑状态
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 
 | package com.bestrookie.design.event;
 import com.bestrookie.ActivityService;
 import com.bestrookie.Status;
 import com.bestrookie.design.State;
 import com.bestrookie.util.Result;
 
 
 
 
 
 
 
 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> currenStatus) {
 return new Result("0001","编辑中不可审批通过");
 }
 
 @Override
 public Result checkRefuse(String activityId, Enum<Status> currenStatus) {
 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","编辑中活动不渴执行活动中变更");
 }
 }
 
 | 
提审状态
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 
 | package com.bestrookie.design.event;
 import com.bestrookie.ActivityService;
 import com.bestrookie.Status;
 import com.bestrookie.design.State;
 import com.bestrookie.util.Result;
 
 
 
 
 
 
 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> currenStatus) {
 ActivityService.execStatus(activityId,currenStatus,Status.Pass);
 return new Result("0000","活动审批完成");
 }
 
 @Override
 public Result checkRefuse(String activityId, Enum<Status> currenStatus) {
 ActivityService.execStatus(activityId,currenStatus,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.Editing);
 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","待审批活动不可执行活动中变更");
 }
 }
 
 | 
这里提供两个具体实现类的内容–编辑状态和提审状态。
状态处理服务
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 
 | package com.bestrookie.design;import com.bestrookie.Status;
 import com.bestrookie.design.event.*;
 import com.bestrookie.util.Result;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 
 
 
 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());
 }
 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);
 }
 }
 
 | 
这是对状态服务的统一控制中心,可以看到在构造函数中提供了所有状态和实现的具体关联,并放到了Map数据结构中。同时,提供了不同名称的接口操作类,让外部调用方可以更加容易地使用此项目功能接口。
TestApi
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | package com.bestrookie.design;import com.alibaba.fastjson.JSON;
 import com.bestrookie.ActivityService;
 import com.bestrookie.Status;
 import com.bestrookie.util.Result;
 
 
 
 
 public class TestApi {
 public static void main(String[] args) {
 String activityId = "100001";
 ActivityService.init(activityId, Status.Editing);
 StateHandler stateHandler = new StateHandler();
 Result result = stateHandler.arraignment(activityId,Status.Editing);
 System.out.println(JSON.toJSON(result));
 Result result1 = stateHandler.open(activityId,Status.Editing);
 System.out.println(JSON.toJSON(result1));
 System.out.println(JSON.toJSON(ActivityService.queryActivityInfo(activityId).getStatus()));
 }
 }
 
 | 
总结
从以上两种方式对一个需求的实现对比可以看到,在使用设计模式处理后,已经没有了if…else,代码的结构也更加清晰,易于扩展。在实现结构的编码方式上,可以看到不再是面向过程的编程,而是面向对象的编程。并且这种设计模式满足了单一职责和开闭原则,当只有满足这种结构时,才会发现代码的扩展是容易的,也就是增加或修改功能不会影响整体。如果状态和各项流传较多,就会产生较多的实现类。因此,可能会给代码的实现增加时间成本,因为如果遇到这种场景可以按需评估投入回报率,主要在于是否会经常修改,是否可以做成组件化,抽离业务功能与非业务功能。