[Серия «Учимся вместе»] Режим состояния: слышали ли вы о режиме «процесс»?

Шаблоны проектирования
[Серия «Учимся вместе»] Режим состояния: слышали ли вы о режиме «процесс»?

намерение

Позволяет объекту изменять свое поведение при изменении его внутреннего состояния

Разговор: позволяет объектам изменять определенные методы привязки, когда они меняют свое состояние.

Рождение государственного образца

[Продукт]: Привет, брат-разработчик, нам нужно разработать娃娃机, вы можете подумать о том, как его оформить заранее.

【Развитие】: Кукольная машина? Я думаю об этом, ему нужно положить монеты, пользователь перемещается, подтверждает выборку и завершает эти действия. Кажется, это очень легко сделать. Используйте переменную для поддержания ее текущей стадии, а затем напишите четыре оператора if.

[БОСС]: Вы собираетесь использовать один основной метод, четыре подметода с оператором if и переменной состояния?

// 伪代码
public void handle() {
    if (flag == A) {
        a();
    }

    if (flag == B) {
        b();
    }
}

[Разработчик]: Да, босс, вы действительно аскариды в моем животе!

[БОСС]: Аскарид, это большая ошибка! , вы хотите привязать этот же метод к слоту для монет, кнопке и джойстику?

[Разработка]: Да, это должны быть разные методы, выставленные пользователям одновременно, я подумаю об этом еще раз

Основной код HeadFirst

"Определите интерфейс состояния, одновременно инкапсулируйте изменения и используйте ключевое слово по умолчанию для инкапсуляции метода по умолчанию."

public interface State {

    /** 投币 **/
    default void giveMoney() {
        System.out.println("无法投币");
    }

    /** 移动滑杆 **/
    default void move() {
        System.out.println("无法移动滑杆");
    }

    /** 抓取 **/
    default void grab() {
        System.out.println("无法抓取");
    }

    void changeState();
}

"Состояние монет Одно из состояний"

public class MoneyState implements State{

    Context context;

    public MoneyState(Context context) {
        this.context = context;
    }

    @Override
    public void giveMoney() {
        System.out.println("已投币!");
        changeState();
    }

    @Override
    public void changeState() {
        context.setExecute(new MoveState(context));
    }
}

Чтобы минимизировать код, показано только одно из состояний.Мы видим, что когда класс состояния MoneyState выполняет бизнес-метод, которому он принадлежит, класс состояния, удерживаемый контекстом, изменяется, что приводит к изменению состояния, и контекст становится более ясным, т. е. мне нужно думать только о том, каково мое следующее состояние.

Идея оформления государственного режима:

  • Контекстный контекст, состояние удержания
  • Интерфейс верхнего уровня состояния
  • ConcreteState Конкретное состояние

Проще говоря,

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

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

Ключ к шаблону состояния

  • Определите все возможные состояния и их переходы
  • Каждое состояние в шаблоне явного состояния может быть представлено пользователю одновременно.

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

Весь паттерн "процесс"

Что, если имя метода одинаково для всех состояний?

В вышеизложенном мы, вероятно, знаем характеристики режима состояния, инкапсулируем состояние в класс и меняем само состояние при вызове метода состояния-ядра.Имена различных методов состояния, рассматриваемых в настоящее время, могут быть разными, предполагая что у всех нас одинаковое имя Как это будет?

Сначала мы столкнемся с проблемой, мы не знаем, сколько раз нужно вызвать метод (потому что могут повторяться случаи A-B), но если цикл бесконечен, контролировать, где он заканчивается, и следует ли продолжить Логотип, кажется, может решить эту проблему.

Приходите на процессный кейс

В двух словах: начните обрабатывать заказы

  • Если это нормально, он перейдет в успешное состояние, будет помещен в хранилище и завершит выполнение.
  • Если это не удается, войдите в состояние отказа, проверьте, нужно ли выполнять повторно, и измените состояние, чтобы обработать заказ.

код выше

"Контекст контекст"

public class Context {

    /**
     * 最大执行次数
     */
    public static final Integer FAIL_NUM = 3;

    /***
     * 失败次数
     */
    private int failNum;

    /**
     * 是否继续执行的标识
     */
    private boolean isAbandon;

    /***
     * 当前状态
     */
    private StateInterface stateInterface;

    public Context() {
        this.stateInterface = new HandleOrder();
        this.failNum = 1;
        this.isAbandon = false;
    }

    /***
     * 处理方法
     */
    public void handle () {
        stateInterface.doAction(this);
    }
    
    // 省略无用代码...
}

"Статус обработки заказа"

public class HandleOrder implements StateInterface {

    @Override
    public void doAction(Context context) {
        printCurrentState();

        // do somethings
        int num = (int) (Math.random() * 11);
        if (num >= 8) {
            System.out.println("处理订单完成, 进入成功状态...");
            context.setStateInterface(new SuccessOrder());
        } else {
            System.out.println("处理订单失败, 进入失败状态...");
            context.setStateInterface(new FailOrder());
        }

        CodeUtils.spilt();
    }

    @Override
    public StateEnums getCurrentState() {
        return StateEnums.HANDLE_ORDER;
    }
}

"метод вызова клиента"

public class App {
    
    public static void main(String[] args) {
        // 模拟从队列中取任务按流程循环执行
        Context context = new Context();
        while (true) {

            // 校验是否为废弃 | 已完成任务
            if (context.isAbandon()) {
                System.out.println("此条任务不再执行... ");
                break;
            }
            
            context.handle();
        }
    }
}

Вывод результатов теста:

# 当前状态:订单处理
# 处理订单失败, 进入失败状态...
# ------------------------

# 当前状态:处理订单失败
# 订单处理失败... 当前执行次数: 1
# ------------------------

# 当前状态:订单处理
# 处理订单失败, 进入失败状态...
# ------------------------

# 当前状态:处理订单失败
# 订单处理失败... 当前执行次数: 2
# ------------------------

# 当前状态:订单处理
# 处理订单完成, 进入成功状态...
# ------------------------

# 当前状态:处理订单成功
# 订单处理完成 -> 进入入库逻辑...
# 入库处理完成
# ------------------------

# 此条任务不再执行... 

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

Сценарии для режима "Процесс"

В таком дизайне правильнее сказать, что это не столько изменение состояния, сколько изменение «процесса», поэтому его можно использовать как решение для многих фоновых задач, особенно при столкновении со многими сценариями бизнес-процессов. , что может значительно улучшить ремонтопригодность кода: мне нужно думать только о своем "потоке"

Принципы дизайна, которым нужно следовать

  • Изменения инкапсуляции: предоставьте метод по умолчанию в родительском интерфейсе, и подкласс может реализовать соответствующий метод состояния.
  • Больше композиции, меньше наследования: шаблон состояния часто противопоставляется шаблону стратегии, оба из которых используют композицию, а не наследование, чтобы расширить свои вариации и возможности.

Какие сценарии подходят для использования режима состояния

  • Поведение объекта зависит от его состояния, и он должен изменять свое поведение во время выполнения в зависимости от состояния.
  • Операция содержит большой условный оператор с несколькими ответвлениями, и эти ответвления зависят от состояния объекта.

Наконец

"Прикрепите диаграмму UML для шаблона состояния в книге GOF:"

Связанные ссылки на код

Адрес GitHub

  • С учетом кейсов в двух классических книгах "HeadFirst" и "GOF"
  • Предоставляет дружественное руководство по чтению