Рекомендации для серверной части | Проект конечного автомата

задняя часть Spring Государственный аппарат дизайн
Рекомендации для серверной части | Проект конечного автомата

Оригинальный адрес:Рекомендации для серверной части | Проект конечного автомата
адрес блога:blog.720ui.com/

В конечном автомате каждое состояние имеет соответствующее поведение, и состояние переключается по мере срабатывания поведения. Один из способов — использовать двумерный массив для реализации механизма конечного автомата, где абсцисса представляет поведение, ордината — состояние, а конкретное значение — текущее состояние.

Мы разрабатываем конечный автомат со сценой входа.

В это время мы проектируем таблицу конечного автомата.

На данный момент это двумерный массив, как показано ниже.

Кроме того, мы также можем реализовать конечный автомат через шаблон состояния. Шаблон состояния инкапсулирует каждое состояние в независимый класс, и конкретное поведение будет меняться вместе с внутренним состоянием. Режим состояния использует классы для представления состояний, поэтому мы можем легко изменять состояние объектов, переключая классы, избегая длинных операторов условного перехода и делая систему более гибкой и расширяемой.

Теперь определим перечисление состояний, которое включает 4 состояния: не подключен, подключен, зарегистрирован, зарегистрирован.

public enum StateEnum {
    // 未连接
    UNCONNECT(1, "UNCONNECT"), 
    // 已连接
    CONNECT(2, "CONNECT"), 
    // 注册中
    REGISTING(3, "REGISTING"), 
    // 已注册
    REGISTED(4, "REGISTED");

    private int key;
    private String value;

    StateEnum(int key, String value) {
        this.key = key;
        this.value = value;
    }
    public int getKey() {return key;}
    public String getValue() {return value;}
}

Определите класс среды, который является объектом, фактически владеющим состоянием.

public class Context {
    private State state;
    public void connect(){
        state.connect(this);
        System.out.println("STATE : " + state.getCurState());
    } 
    public void register(){
        state.register(this);
        System.out.println("STATE : " + state.getCurState());
    }   
    public void registerSuccess(){
        state.registerSuccess(this);
        System.out.println("STATE : " + state.getCurState());
    }
    public void registerFailed(){
        state.registerFailed(this);
        System.out.println("STATE : " + state.getCurState());
    }
    public void unRegister(){
        state.unRegister(this);
        System.out.println("STATE : " + state.getCurState());
    }
    public State getState() {
        return state;
    }
    public void setState(State state) {
        this.state = state;
    }
}

Шаблон состояния использует классы для представления состояний, так что мы можем легко изменить состояние объекта, переключая классы. Теперь мы определяем несколько классов состояний.

public interface State {
    void connect(Context c);
    void register(Context c);
    void registerSuccess(Context c);
    void registerFailed(Context c);
    void unRegister(Context c);
    String getCurState();
}

public class UnconnectState implements State {
    @Override
    public void connect(Context c) {
        c.setState(new ConnectState());
    }
    @Override
    public void register(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void registerSuccess(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void registerFailed(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void unRegister(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public String getCurState() {
        return StateEnum.UNCONNECT.toString();
    }
}

public class ConnectState implements State {
    @Override
    public void connect(Context c) {
        c.setState(new ConnectState());
    }
    @Override
    public void register(Context c) {
        c.setState(new RegistingState());
    }
    @Override
    public void registerSuccess(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void registerFailed(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void unRegister(Context c) {
        c.setState(new UnconnectState());
    }
    @Override
    public String getCurState() {
        return StateEnum.CONNECT.toString();
    }
}

public class RegistingState implements State {
    @Override
    public void connect(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void register(Context c) {
        c.setState(new RegistingState());
    }
    @Override
    public void registerSuccess(Context c) {
        c.setState(new RegistedState());
    }
    @Override
    public void registerFailed(Context c) {
        c.setState(new UnconnectState());
    }
    @Override
    public void unRegister(Context c) {
        c.setState(new UnconnectState());
    }
    @Override
    public String getCurState() {
        return StateEnum.REGISTING.toString();
    }
}

public class RegistedState implements State {
    @Override
    public void connect(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void register(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void registerSuccess(Context c) {
        c.setState(new RegistedState());
    }
    @Override
    public void registerFailed(Context c) {
        throw new RuntimeException("INVALID_OPERATE_ERROR");
    }
    @Override
    public void unRegister(Context c) {
        c.setState(new UnconnectState());
    }
    @Override
    public String getCurState() {
        return StateEnum.REGISTED.toString();
    }
}

Обратите внимание, что мы можем сгенерировать исключение RuntimeException, если действие не вызывает изменение состояния. Кроме того, при вызове переключение состояния контролируется классом среды, как показано ниже.

public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        context.connect();
        context.register();
    }
}

Spring StateMachine делает структуру конечного автомата более иерархической, что может помочь разработчикам упростить процесс разработки конечных автоматов. Теперь давайте модернизируем Spring StateMachine. Измените файл pom и добавьте зависимости Maven.

<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>1.2.0.RELEASE</version>
</dependency>

Определите перечисление состояний, которое включает 4 состояния: не подключено, подключено, зарегистрировано и зарегистрировано.

public enum RegStatusEnum {
    // 未连接
    UNCONNECTED,
    // 已连接
    CONNECTED,
    // 注册中
    REGISTERING,
    // 已注册
    REGISTERED;
}

Определите перечисление поведения, включая подключение, регистрацию, успешную регистрацию, ошибку регистрации, выход из системы 5 видов событий поведения.

public enum RegEventEnum {
    // 连接
    CONNECT,
    // 注册
    REGISTER,
    // 注册成功
    REGISTER_SUCCESS,
    // 注册失败
    REGISTER_FAILED,
    // 注销
    UN_REGISTER;
}

Далее нам нужно настроить конечный автомат, где аннотация @EnableStateMachine идентифицирует и включает функцию конечного автомата Spring StateMachine.

@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<RegStatusEnum, RegEventEnum> {
}

Нам нужно инициализировать состояние государства. Среди них, начальный (Regstatusenum.unned) определяет, что исходное состояние является нескрытым государством. Штаты (enumset.allof (regstatusenum.class)) определяет все состояния, присутствующие в штате.

@Override
public void configure(StateMachineStateConfigurer<RegStatusEnum, RegEventEnum> states) throws Exception {
    states.withStates()
    // 定义初始状态
    .initial(RegStatusEnum.UNCONNECTED)
    // 定义状态机状态
    .states(EnumSet.allOf(RegStatusEnum.class));
}

Что нам нужно для инициализации текущего состояния конечного автомата, там событие. Какой источник определяет исходное состояние, целевое целевое состояние, событие, указанное триггерным событием.

@Override
public void configure(StateMachineTransitionConfigurer<RegStatusEnum, RegEventEnum> transitions)
        throws Exception {
        // 1.连接事件
        // 未连接 -> 已连接
        .withExternal()
            .source(RegStatusEnum.UNCONNECTED)
            .target(RegStatusEnum.CONNECTED)
            .event(RegEventEnum.CONNECT)
        .and() 
        .withExternal()
            .source(RegStatusEnum.CONNECTED)
            .target(RegStatusEnum.CONNECTED)
            .event(RegEventEnum.CONNECT)
        .and()                    

        // 2.注册事件   
        // 已连接 -> 注册中
        .withExternal()
            .source(RegStatusEnum.CONNECTED)
            .target(RegStatusEnum.REGISTERING)
            .event(RegEventEnum.REGISTER)
        .and()
       .withExternal()
            .source(RegStatusEnum.REGISTERING)
            .target(RegStatusEnum.REGISTERING)
            .event(RegEventEnum.REGISTER)
        .and() 

        // 3.注册成功事件   
        // 注册中 -> 已注册
        .withExternal()
            .source(RegStatusEnum.REGISTERING)
            .target(RegStatusEnum.REGISTERED)
            .event(RegEventEnum.REGISTER_SUCCESS)
        .and()
        .withExternal()
            .source(RegStatusEnum.REGISTERED)
            .target(RegStatusEnum.REGISTERED)
            .event(RegEventEnum.REGISTER_SUCCESS)
        .and()

         // 4.注册失败事件   
        // 注册中 -> 未连接
        .withExternal()
            .source(RegStatusEnum.REGISTERING)
            .target(RegStatusEnum.UNCONNECTED)
            .event(RegEventEnum.REGISTER_FAILED)
        .and()

        // 5.注销事件
        // 已连接 -> 未连接
        .withExternal()
            .source(RegStatusEnum.CONNECTED)
            .target(RegStatusEnum.UNCONNECTED)
            .event(RegEventEnum.UN_REGISTER)
        .and()
        // 注册中 -> 未连接
        .withExternal()
            .source(RegStatusEnum.REGISTERING)
            .target(RegStatusEnum.UNCONNECTED)
            .event(RegEventEnum.UN_REGISTER)
        .and()
        // 已注册 -> 未连接
        .withExternal()
            .source(RegStatusEnum.REGISTERED)
            .target(RegStatusEnum.UNCONNECTED)
            .event(RegEventEnum.UN_REGISTER)
        ;
}

Spring StateMachine предоставляет метод реализации конфигурации аннотаций.Все события, определенные в интерфейсе StateMachineListener, можно настроить и реализовать с помощью аннотаций. Возьмем в качестве примера событие подключения. В @OnTransition источник указывает исходное состояние, а цель указывает целевое состояние. Когда событие инициируется, оно будет отслеживаться, и будет вызываться метод connect().

@WithStateMachine
public class StateMachineEventConfig {

    @OnTransition(source = "UNCONNECTED", target = "CONNECTED")
    public void connect() {
        System.out.println("///////////////////");
        System.out.println("连接事件, 未连接 -> 已连接");
        System.out.println("///////////////////");
    }

    @OnTransition(source = "CONNECTED", target = "REGISTERING")
    public void register() {
        System.out.println("///////////////////");
        System.out.println("注册事件, 已连接 -> 注册中");
        System.out.println("///////////////////");
    }

    @OnTransition(source = "REGISTERING", target = "REGISTERED")
    public void registerSuccess() {
        System.out.println("///////////////////");
        System.out.println("注册成功事件, 注册中 -> 已注册");
        System.out.println("///////////////////");
    }

    @OnTransition(source = "REGISTERED", target = "UNCONNECTED")
    public void unRegister() {
        System.out.println("///////////////////");
        System.out.println("注销事件, 已注册 -> 未连接");
        System.out.println("///////////////////");
    }
}

Spring StateMachine делает структуру конечного автомата более иерархической Давайте рассмотрим следующие несколько основных шагов: Первый шаг — определить перечисление состояний. Второй шаг — определить перечисление событий. Третий шаг — определить конфигурацию конечного автомата, установить начальное состояние и взаимосвязь между состоянием и событием. Четвертый шаг — определить прослушиватель состояния, который запускает метод при изменении состояния.

(Заканчивать)

Другие интересные статьи можно найти в общедоступном аккаунте WeChat «Server Thinking»!