状态机

在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());
    }
}

6.第六版状态机

在Java中实现一个状态机来管理异步流程,你可以定义一个枚举来表示所有可能的状态,一个状态转换表来定义状态之间的转换,以及一个状态机类来管理状态转换和执行相关的动作。以下是一个简化的例子,展示了如何实现这样的状态机。

public enum ProcessState {
    INITIAL,
    FETCH_CODE,
    CREATE_REPO,
    PUSH_REPO,
    ADD_AUTHORIZATION,
    CREATE_APPLICATION,
    CREATE_GROUP,
    CREATE_BUILD_TASK,
    EXECUTE_BUILD_TASK,
    GET_BUILD_RESULTS,
    CREATE_DEPLOYMENT_PLAN,
    DEPLOY_CODE,
    FINISHED,
    ERROR // 特殊状态,表示出错
}

状态转换的接口和事件的枚举

public interface StateTransition {
    ProcessState nextState(Event event);
}

public enum Event {
    CODE_FETCHED,
    REPO_CREATED,
    REPO_PUSHED,
    AUTHORIZATION_ADDED,
    APPLICATION_CREATED,
    GROUP_CREATED,
    BUILD_TASK_CREATED,
    BUILD_TASK_EXECUTED,
    BUILD_RESULTS_OBTAINED,
    DEPLOYMENT_PLAN_CREATED,
    CODE_DEPLOYED,
    ERROR_OCCURRED // 特殊事件,表示出错
}

现在,我们可以创建一个状态机类,它将使用一个状态转换表来决定如何在状态之间转换,并执行相关的动作:

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

public class ProcessStateMachine {

    private ProcessState currentState;
    private final Map<ProcessState, Map<Event, ProcessState>> transitionTable;
    private final Map<ProcessState, Consumer<ProcessState>> actions;

    public ProcessStateMachine() {
        this.currentState = ProcessState.INITIAL;
        this.transitionTable = new HashMap<>();
        this.actions = new HashMap<>();
        initializeTransitionTable();
        initializeActions();
    }

    private void initializeTransitionTable() {
        // 你需要根据实际的流程配置这个转换表
        // 示例:从INITIAL状态,在收到start事件时,转移到FETCH_CODE状态
        Map<Event, ProcessState> initialTransitions = new HashMap<>();
        initialTransitions.put(Event.CODE_FETCHED, ProcessState.FETCH_CODE);
        // ... 添加其他转换规则
        transitionTable.put(ProcessState.INITIAL, initialTransitions);
        // ... 对其他状态进行初始化
    }

    private void initializeActions() {
        // 定义每个状态的动作,例如发送更新到前端
        actions.put(ProcessState.FETCH_CODE, this::fetchCodeAction);
        // ... 定义其他状态的动作
    }

    private void fetchCodeAction(ProcessState state) {
        // 执行拉取代码的动作,可能包括异步调用等
        // 更新前端状态等
    }

    public synchronized void fireEvent(Event event) {
        Map<Event, ProcessState> possibleTransitions = transitionTable.get(currentState);
        if (possibleTransitions == null) {
            handleInvalidTransition(event);
            return;
        }

        ProcessState nextState = possibleTransitions.get(event);
        if (nextState == null) {
            handleInvalidTransition(event);
            return;
        }

        // 执行状态转换前的动作
        performAction(nextState);

        // 更新状态
        this.currentState = nextState;
    }

    private void performAction(ProcessState nextState) {
        Consumer<ProcessState> action = actions.get(nextState);
        if (action != null) {
            action.accept(nextState);
        }
    }

    private void handleInvalidTransition(Event event) {
        // 处理无效转换,可能是设置错误状态,记录日志等
        this.currentState = ProcessState.ERROR;
        // ... 执行错误处理的动作
    }

    public ProcessState getCurrentState() {
        return currentState;
    }
}

在这个状态机类中,你需要根据实际需求来填充initializeTransitionTable方法中的状态转换表,以及initializeActions方法中的状态动作。每个状态动作可能包括向前端发送状态更新、进行异步调用等。

这个状态机的设计是线程安全的,因为fireEvent方法是同步的。这意味着在多线程环境中,状态转换不会同时发生。

要使用这个状态机,你的后端代码需要在适当的时候触发事件:

ProcessStateMachine stateMachine = new ProcessStateMachine();
// ... 在某个地方,当代码拉取完成时
stateMachine.fireEvent(Event.CODE_FETCHED);
// ... 状态机会自动转移到下一个状态,并执

请注意,这个代码是一个简化示例,实际实现时你需要根据具体业务逻辑来填充转换表和状态动作。此外,错误处理和持久化状态的逻辑也需要你根据实际情况设计和实现。

posted @ 2024-03-13 10:51  SpecialSpeculator  阅读(28)  评论(0编辑  收藏  举报