在Java程序中实现一个状态机,你可以采用几种不同的设计模式。
1.第一版状态机
下面是一个简单的状态机实现,使用了枚举类型来表示状态,并且使用了状态模式(State Pattern)来实现状态之间的转换。
public enum ProcessState {
STARTED,
IN_PROGRESS,
FINISHED
}
接下来,定义状态机的上下文环境,它将持有当前的状态,并允许你改变状态:
public class ProcessContext {
private ProcessState currentState;
public ProcessContext() {
currentState = ProcessState.STARTED; // 初始状态
}
public void setCurrentState(ProcessState state) {
this.currentState = state;
}
public ProcessState getCurrentState() {
return currentState;
}
public void next() {
switch (currentState) {
case STARTED:
setCurrentState(ProcessState.IN_PROGRESS);
break;
case IN_PROGRESS:
setCurrentState(ProcessState.FINISHED);
break;
case FINISHED:
System.out.println("Process is already finished.");
break;
}
}
}
在这个例子中,ProcessContext 类拥有一个 currentState 属性来跟踪当前的状态,并有一个 next 方法来转移到下一个状态。这是一个非常简单的状态机,它假定状态的转换是线性的(从开始到进行中,再到结束)。
2.第二种状态机
如果你的状态转换更加复杂,或者你需要在状态转换时执行更多的逻辑,你可以为每个状态创建一个单独的类,并实现一个共同的接口。这样,每个状态都可以有自己的转换逻辑。这是状态模式的一个例子:
public interface ProcessState {
void next(ProcessContext context);
}
public class StartedState implements ProcessState {
@Override
public void next(ProcessContext context) {
// 在这里实现从开始到进行中状态的逻辑
context.setCurrentState(new InProgressState());
}
}
public class InProgressState implements ProcessState {
@Override
public void next(ProcessContext context) {
// 在这里实现从进行中到结束状态的逻辑
context.setCurrentState(new FinishedState());
}
}
public class FinishedState implements ProcessState {
@Override
public void next(ProcessContext context) {
// 已经是结束状态,可能不需要做任何事情,或者抛出一个异常
System.out.println("Process is already finished.");
}
}
public class ProcessContext {
private ProcessState currentState;
public ProcessContext() {
currentState = new StartedState(); // 初始状态
}
public void setCurrentState(ProcessState state) {
this.currentState = state;
}
public void next() {
currentState.next(this);
}
}
在这个状态模式的实现中,每个状态都是一个实现了 ProcessState 接口的类。这样,每个状态类都可以有自己的 next 方法,负责处理状态转换的逻辑。ProcessContext 类持有一个 ProcessState 类型的对象,代表当前的状态,并通过调用当前状态的 next 方法来进行状态转换。
在实际应用中,状态模式可以很好地处理更复杂的状态机逻辑,每个状态可以有自己的行为,甚至可以根据不同的条件转移到不同的状态。这种设计也更容易扩展和维护。
3.第三种状态机
可以直接跳到某个状态,接着执行后面的流程
要设计一个可以灵活跳过步骤的状态机,我们可以定义一个状态接口和一系列具体的状态类,每个状态类负责定义它的下一个状态。我们还需要一个上下文类来管理状态的转换,并允许跳过状态。下面是一个简单的实现:
1.定义状态接口:
public interface TaskState {
void goToNextStep(TaskContext context);
// 可能还需要定义其他方法,比如处理任务、撤销任务等
}
2.实现具体的状态类:
public class InitializedState implements TaskState {
@Override
public void goToNextStep(TaskContext context) {
context.setCurrentState(new CreateRepositoryState());
}
}
public class CreateRepositoryState implements TaskState {
@Override
public void goToNextStep(TaskContext context) {
context.setCurrentState(new PullCodeState());
}
}
// ... 其他状态类实现 ...
public class SmsNotificationState implements TaskState {
@Override
public void goToNextStep(TaskContext context) {
System.out.println("Process is finished.");
}
}
3.上下文类
public class TaskContext {
private TaskState currentState;
public TaskContext() {
currentState = new InitializedState(); // 初始状态
}
public void setCurrentState(TaskState state) {
this.currentState = state;
}
public TaskState getCurrentState() {
return currentState;
}
public void nextStep() {
currentState.goToNextStep(this);
}
// 直接跳到某一个状态
public void skipToState(TaskState state) {
setCurrentState(state);
}
}
4.使用状态机:
public class TaskStateMachineDemo {
public static void main(String[] args) {
TaskContext context = new TaskContext();
// 正常流程
context.nextStep(); // 创建代码库
context.nextStep(); // 拉取代码
context.nextStep(); // 推送代码
context.nextStep(); // 构建代码
context.nextStep(); // 部署运行
context.nextStep(); // 短信通知
// 如果需要跳过某些步骤,直接设置目标状态
context.skipToState(new DeployAndRunState()); // 直接跳转到部署运行
context.nextStep(); // 短信通知
}
}
在这个设计中,TaskContext 类提供了 nextStep 方法来移动到下一个状态,以及 skipToState 方法来跳过一个或多个状态。这种设计提供了很高的灵活性,允许你根据需要跳过某些步骤。
请注意,这个例子非常简化,实际的实现可能需要考虑错误处理、状态持久化、状态回滚等复杂情况。每个状态类中的 goToNextStep 方法可以包含实际的业务逻辑,例如创建代码库、拉取代码等操作。如果业务逻辑复杂,可以将这些逻辑移到单独的服务类中,以保持状态类的简洁。
4.第四种状态机
添加处理错误、状态持久化和状态回滚等复杂情况,我们需要在设计中加入异常处理、事务管理以及状态的保存和恢复机制。以下是对上述代码的优化,以支持这些复杂场景:
异常处理 – 在状态转换和任务执行过程中加入异常处理机制。
状态持久化 – 在状态转换时保存状态,以便系统在发生故障时能从上次已知状态恢复。
状态回滚 – 如果在状态转换过程中发生错误,提供一个机制来回滚到前一个稳定状态。
1.定义状态接口,增加异常处理和回滚方法:
public interface TaskState {
void goToNextStep(TaskContext context) throws TaskStateException;
void rollback(TaskContext context) throws TaskStateException;
}
2.实现具体的状态类,并在每个状态类中处理异常和回滚:
public class InitializedState implements TaskState {
@Override
public void goToNextStep(TaskContext context) throws TaskStateException {
try {
// 执行状态转换前的逻辑,比如资源初始化等
context.saveState(this); // 状态持久化
context.setCurrentState(new CreateRepositoryState());
// 执行状态转换后的逻辑,可能包括一些业务操作
} catch (Exception e) {
throw new TaskStateException("Error occurred in InitializedState", e);
}
}
@Override
public void rollback(TaskContext context) throws TaskStateException {
// 在这里实现回滚逻辑
}
}
3.增强上下文类,加入异常处理、状态持久化和回滚机制:
public class TaskContext {
private TaskState currentState;
private TaskState previousState; // 用于回滚
public TaskContext() {
currentState = new InitializedState(); // 初始状态
}
public void setCurrentState(TaskState state) {
this.previousState = this.currentState;
this.currentState = state;
saveState(state);
}
public void nextStep() {
try {
currentState.goToNextStep(this);
} catch (TaskStateException e) {
// 处理异常,可能需要回滚到前一个状态
rollback();
}
}
public void skipToState(TaskState state) {
setCurrentState(state);
nextStep();
}
public void rollback() {
if (previousState != null) {
try {
previousState.rollback(this);
setCurrentState(previousState);
} catch (TaskStateException e) {
// 处理回滚异常
}
}
}
public void saveState(TaskState state) {
// 实现状态持久化的逻辑,比如写入数据库或文件等
}
public void restoreState() {
// 实现状态恢复的逻辑,从持久化存储中恢复状态
}
}
4.异常类:
public class TaskStateException extends Exception {
public TaskStateException(String message, Throwable cause) {
super(message, cause);
}
}
在这个优化后的设计中,每个状态类负责处理它的状态转换逻辑,并包含错误处理和回滚逻辑。TaskContext 类管理状态的持久化和恢复,并处理状态转换过程中的异常和回滚操作。
这个设计允许任务在发生异常时安全地回滚到之前的状态,并且能够在系统重启后从持久化存储中恢复状态,继续执行。这样的设计使得状态机更加健壮和可靠,能够处理复杂的业务场景。
5.状态机优化
避免使用状态机的时候context.nextStep()重复出现
优化TaskContext
public class TaskContext {
private TaskState currentState;
private TaskState previousState; // 用于回滚
public TaskContext() {
currentState = new InitializedState(); // 初始状态
}
public void setCurrentState(TaskState state) {
this.previousState = this.currentState;
this.currentState = state;
saveState(state);
}
public void nextStep() {
try {
currentState.goToNextStep(this);
} catch (TaskStateException e) {
// 处理异常,可能需要回滚到前一个状态
rollback();
}
}
public void skipToState(TaskState state) {
setCurrentState(state);
nextStep();
}
public void rollback() {
if (previousState != null) {
try {
previousState.rollback(this);
setCurrentState(previousState);
} catch (TaskStateException e) {
// 处理回滚异常
}
}
}
public void saveState(TaskState state) {
// 实现状态持久化的逻辑,比如写入数据库或文件等
}
public void restoreState() {
// 实现状态恢复的逻辑,从持久化存储中恢复状态
}
public void executeAllSteps() {
while (currentState != null && !(currentState instanceof FinalState)) {
nextStep();
}
}
}
这里的FinalState是一个特殊的状态,表示任务流程的结束。如果你的状态机设计中没有明确的结束状态,你可能需要调整这个条件以适应你的设计。
状态机的使用案例
public class TaskStateMachineDemo {
public static void main(String[] args) {
TaskContext context = new TaskContext();
try {
// 恢复之前的状态,如果有的话
context.restoreState();
// 自动执行所有步骤
context.executeAllSteps();
} catch (TaskStateException e) {
// 异常处理逻辑
System.err.println("An error occurred: " + e.getMessage());
// 可能需要进一步的错误恢复或通知逻辑
}
}
}
在这个新的设计中,executeAllSteps()方法将自动处理所有状态转换,直到任务完成或遇到异常。这样,客户端代码就不需要显式地多次调用nextStep(),从而减少了冗余并提高了代码的可读性。
如果你需要在某些状态之间进行更复杂的流程控制,比如条件跳转或循环,你可能需要在TaskContext中实现更复杂的逻辑来处理这些情况。这可能包括引入一个状态图或决策表来定义状态转换的条件和顺序。
6.基于事件的状态机
首先,我们需要一个类来表示任务上下文(TaskContext),这个类将持有状态机的实例,并且可能还会持有其他与任务相关的数据。在这个例子中,我们将简单地将 StateMachine 作为 TaskContext 的一部分。
以下是改造后的代码
状态机
public class StateMachine {
private State currentState;
public StateMachine() {
currentState = State.INIT;
}
public void handleEvent(Event event) {
switch (currentState) {
case INIT:
if (event == Event.START) {
transitionToRunning();
}
break;
case RUNNING:
if (event == Event.COMPLETE) {
transitionToFinish();
} else if (event == Event.FAIL) {
transitionToInit();
}
break;
case FINISH:
// 在 FINISH 状态通常不会有事件处理,除非你想要添加重置逻辑
break;
}
}
private void transitionToRunning() {
System.out.println("Transitioning from INIT to RUNNING");
currentState = State.RUNNING;
// 这里可以添加进入RUNNING状态时需要执行的代码
}
private void transitionToFinish() {
System.out.println("Transitioning from RUNNING to FINISH");
currentState = State.FINISH;
// 这里可以添加进入FINISH状态时需要执行的代码
}
private void transitionToInit() {
System.out.println("Transitioning from RUNNING to INIT due to failure");
currentState = State.INIT;
// 这里可以添加因失败而重置到INIT状态时需要执行的代码
}
public State getCurrentState() {
return currentState;
}
}
TaskContext
public class TaskContext {
private StateMachine stateMachine;
public TaskContext() {
this.stateMachine = new StateMachine();
}
public void fireEvent(Event event) {
stateMachine.handleEvent(event);
}
public State getCurrentState() {
return stateMachine.getCurrentState();
}
}
测试代码
public class StateMachineDemo {
public static void main(String[] args) {
TaskContext taskContext = new TaskContext();
// 触发事件,从 INIT 到 RUNNING
taskContext.fireEvent(Event.START);
System.out.println("当前状态: " + taskContext.getCurrentState());
// 假设任务完成,触发事件,从 RUNNING 到 FINISH
taskContext.fireEvent(Event.COMPLETE);
System.out.println("当前状态: " + taskContext.getCurrentState());
}
}