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

Абстрактный конечный автомат

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c483626f-7d32-41a3-a3cc-8cb30dd40e5b/Untitled.png

В англоязычной литературе термин известен, как FSM - Finite State Machine.

АКА - абстрактный автомат, число возможных состояний которого конечно [1]. Абстрактный автомат - математическая абстракция, модель дискретного устройства, имеющего один вход, один выход и в каждый момент времени находящийся в одном состоянии из множества возможных [2].

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

Приведем более конкретный пример. Допустим, вам нужно построить систему, которая определяет может ли человек получить доступ к некоторому ресурсу. Сначала система должна проверить есть ли у нее в локальном кэше необходимые данные, если нет то сходить на удаленный сервер, дождаться результата и обновить кэш. Далее получив из кэша эти данные, применить некоторую бизнес логику, от результата которой зависит, можно ли пользователю открыть доступ или нет. Казалось бы, можно это написать в лоб, но автомат будет выглядеть намного изящнее, и что самое главное для нас сейчас - автомат легко расширяем.

Для того чтобы достичь простоты расширения автоматов, необходимо разработать абстрактный автомат, реализация которого будет общая для многих задач. Тогда конкретная задача - это описание правил перехода из одного состояния в другое.

Основные идеи

Перечислим основные концепции:

Ситуации

Когда необходимо применять автомат? Как минимум, когда у вас начинают получаться конструкции похожие на эту[6]:

switch( state ) {
    case RADIO:
        switch(event) {
            case mode:
              /* Change the state */
              state = CD;
              break;
            case next:
              /* Increase the station number */
              stationNumber++;
              break;
        }
        break;
    case CD:
        switch(event) {
            case mode:
              /* Change the state */
              state = RADIO;
              break;
            case next:
              /* Go to the next track */
              trackNumber++;
              break;
        }
        break;
}

Мы видим здесь, что система может находиться в 4 состояниях, между которыми можно как-то переключаться. Чем плох этот код, он же решает свою задачу? Основная проблема - невозможность расширения кода. Добавление нового состояния, например MP3, или кнопки pause, привёдет к необходимости вдумчиво читать код с вложенными switch блоками, анализировать к чему приведёт новое состояние и так далее. Этого можно избежать если разделить код на составляющие: собственно код, отвечающий за переходы между состояниями и код - обработчиков самих состояний.

Тогда нам нужны: