经典案例:任务状态流转的过程
private String state = StateEnum.INIT.getStateValue();
public String update(ActionEnum actionEnum){if (StateEnum.INIT.getStateValue().equals(state)) {//初始化 -> 进行中if (ActionEnum.START == actionEnum) {state = StateEnum.ONGOING.getStateValue();}} else if (StateEnum.ONGOING.getStateValue().equals(state)) {//进行中 -> 已完成 进行中 -> 暂停中 进行中 -> 已过期if (actionEnum == ActionEnum.ACHIEVE) {state = StateEnum.FINISHED.getStateValue();}else if (actionEnum == ActionEnum.STOP){state = StateEnum.PAUSED.getStateValue();}else if (actionEnum == ActionEnum.EXPIRE){state = StateEnum.EXPIRED.getStateValue();}}else if (StateEnum.PAUSED.getStateValue().equals(state)) {//暂停中 -> 进行中 暂停中 -> 已过期if (actionEnum == ActionEnum.START) {state = StateEnum.ONGOING.getStateValue();}else if (actionEnum == ActionEnum.EXPIRE){state = StateEnum.EXPIRED.getStateValue();}}else if (StateEnum.FINISHED.getStateValue().equals(state)) {}else if (StateEnum.EXPIRED.getStateValue().equals(state)) {}return state;
}
不难看出,状态流转的过程使用了大量的if-else做判断,条件复杂或者状态太多时,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的if-else语句,这违背了开闭原则,不利于程序的扩展。那么可以使用状态模式解决以上的问题,参考美团:设计模式二三事。
状态模式:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。状态模式包含以下主要角色:
环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
/*** @author maoyouhua* @time 2024/3/19* @jdkversion jdk21* * 枚举类*/
@AllArgsConstructor
@Getter
public enum ActionEnum {START( "开始"),STOP("暂停"),ACHIEVE("完成"),EXPIRE("过期");private final String actionValue;
}
@AllArgsConstructor
@Getter
public enum StateEnum {INIT("初始化"),ONGOING( "进行中"),PAUSED("暂停中"),FINISHED("已完成"),EXPIRED("已过期");private final String stateValue;public String getStateValue() {return stateValue;}
}
/*** @author maoyouhua* @time 2024/3/19* @jdkversion jdk21* * 抽象状态*/
public abstract interface State {void update(TaskService taskService, ActionEnum actionEnum);String getCurrentState();
}
public abstract class AbstractState implements State {}
/*** @author maoyouhua* @time 2024/3/19* @jdkversion jdk21* * 具体状态* 此处有spring循环依赖的问题 需要加配置 spring.main.allow-circular-references=true*/
@Component
public class InitState extends AbstractState {@Autowiredprivate OnGoingState onGoingState;public static final String stateValue = StateEnum.INIT.getStateValue();@Overridepublic void update(TaskService taskService, ActionEnum actionEnum) {//初始化 -> 进行中if (actionEnum == ActionEnum.START) {taskService.setTaskState(onGoingState);}}@Overridepublic String getCurrentState() {return stateValue;}
}
@Component
public class OnGoingState extends AbstractState {@Autowiredprivate FinishedState finishedState;@Autowiredprivate ExpiredState expiredState;@Autowiredprivate PausedState pausedState;public static final String stateValue = StateEnum.ONGOING.getStateValue();@Overridepublic void update(TaskService taskService, ActionEnum actionEnum) {//进行中 -> 已完成 进行中 -> 暂停中 进行中 -> 已过期if (actionEnum == ActionEnum.ACHIEVE) {taskService.setTaskState(finishedState);}else if (actionEnum == ActionEnum.STOP){taskService.setTaskState(pausedState);}else if (actionEnum == ActionEnum.EXPIRE){taskService.setTaskState(expiredState);}}@Overridepublic String getCurrentState() {return stateValue;}
}
@Component
public class PausedState extends AbstractState {@Autowiredprivate OnGoingState onGoingState;@Autowiredprivate ExpiredState expiredState;public static final String stateValue = StateEnum.PAUSED.getStateValue();@Overridepublic void update(TaskService taskService, ActionEnum actionEnum) {// 暂停中 -> 进行中 暂停中 -> 已过期if (actionEnum == ActionEnum.START) {taskService.setTaskState(onGoingState);}else if (actionEnum == ActionEnum.EXPIRE){taskService.setTaskState(expiredState);}}@Overridepublic String getCurrentState() {return stateValue;}
}
@Component
public class FinishedState extends AbstractState {public static final String stateValue = StateEnum.FINISHED.getStateValue();@Overridepublic void update(TaskService taskService, ActionEnum actionEnum) {}@Overridepublic String getCurrentState() {return stateValue;}
}
@Component
public class ExpiredState extends AbstractState {public static final String stateValue = StateEnum.EXPIRED.getStateValue();@Overridepublic void update(TaskService taskService, ActionEnum actionEnum) {}@Overridepublic String getCurrentState() {return stateValue;}
}
/*** @author maoyouhua* @time 2024/3/19* @jdkversion jdk21** 多线程不安全*/
public interface TaskService {void updateState(ActionEnum actionEnum);State getTaskState();void setTaskState(State state);
}
@Component
public class TaskServiceImpl implements TaskService {private State state;@Autowiredpublic TaskServiceImpl(InitState initState) {this.state = initState;}@Overridepublic void updateState(ActionEnum actionEnum){state.update(this,actionEnum);}@Overridepublic State getTaskState() {return state;}@Overridepublic void setTaskState(State state) {this.state = state;}}
/*** @author maoyouhua* @time 2024/3/19* @jdkversion jdk21*/
@RestController
@RequestMapping("/task")
public class TaskController {private String state = StateEnum.INIT.getStateValue();@Autowiredprivate TaskService taskService;@RequestMapping("/test")public String test(){return "任务测试";}/**** @param actionEnum START、STOP、ACHIEVE、EXPIRE* @return*/@RequestMapping("/update")public String update(ActionEnum actionEnum){if (StateEnum.INIT.getStateValue().equals(state)) {//初始化 -> 进行中if (ActionEnum.START == actionEnum) {state = StateEnum.ONGOING.getStateValue();}} else if (StateEnum.ONGOING.getStateValue().equals(state)) {//进行中 -> 已完成 进行中 -> 暂停中 进行中 -> 已过期if (actionEnum == ActionEnum.ACHIEVE) {state = StateEnum.FINISHED.getStateValue();}else if (actionEnum == ActionEnum.STOP){state = StateEnum.PAUSED.getStateValue();}else if (actionEnum == ActionEnum.EXPIRE){state = StateEnum.EXPIRED.getStateValue();}}else if (StateEnum.PAUSED.getStateValue().equals(state)) {//暂停中 -> 进行中 暂停中 -> 已过期if (actionEnum == ActionEnum.START) {state = StateEnum.ONGOING.getStateValue();}else if (actionEnum == ActionEnum.EXPIRE){state = StateEnum.EXPIRED.getStateValue();}}else if (StateEnum.FINISHED.getStateValue().equals(state)) {}else if (StateEnum.EXPIRED.getStateValue().equals(state)) {}return state;}@RequestMapping("/updateByStateMode")public String updateByStateMode(ActionEnum actionEnum){taskService.updateState(actionEnum);return taskService.getTaskState().getCurrentState();}
}