Перейти к основному содержимому

Реализация простых автоматов состояний с перечислениями Java

· 5 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим конечные автоматы и то, как их можно реализовать в Java с помощью Enums.

Мы также объясним преимущества этой реализации по сравнению с использованием интерфейса и конкретного класса для каждого состояния.

2. Перечисления Java

Java Enum — это особый тип класса, определяющий список констант. Это позволяет реализовать типобезопасную реализацию и сделать код более читаемым .

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

Простейшее перечисление, которое содержит состояния запроса на отпуск:

public enum LeaveRequestState {
Submitted,
Escalated,
Approved
}

Мы можем обратиться к константам этого перечисления:

LeaveRequestState state = LeaveRequestState.Submitted;

Перечисления также могут содержать методы. Мы можем написать абстрактный метод в перечислении, что заставит каждый экземпляр перечисления реализовать этот метод. Это очень важно для реализации конечных автоматов, как мы увидим ниже.

Поскольку перечисления Java неявно расширяют класс java.lang.Enum , они не могут расширять другой класс. Однако они могут реализовать интерфейс, как и любой другой класс.

Вот пример перечисления, содержащего абстрактный метод:

public enum LeaveRequestState {
Submitted {
@Override
public String responsiblePerson() {
return "Employee";
}
},
Escalated {
@Override
public String responsiblePerson() {
return "Team Leader";
}
},
Approved {
@Override
public String responsiblePerson() {
return "Department Manager";
}
};

public abstract String responsiblePerson();
}

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

В данном случае мы расширили первый пример с помощью метода ответственного человека() . Это говорит нам о человеке, ответственном за выполнение каждого действия. Итак, если мы попытаемся проверить человека, ответственного за состояние Escalated , он выдаст нам «Team Leader»:

LeaveRequestState state = LeaveRequestState.Escalated;
assertEquals("Team Leader", state.responsiblePerson());

Таким же образом, если мы проверим, кто отвечает за утверждение запроса, он выдаст нам «Менеджер отдела»:

LeaveRequestState state = LeaveRequestState.Approved;
assertEquals("Department Manager", state.responsiblePerson());

3. Конечные автоматы

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

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

Шаблон состояния — один из хорошо известных двадцати трех шаблонов проектирования GoF. Этот шаблон заимствует концепцию из модели в математике. Он позволяет объекту инкапсулировать различное поведение одного и того же объекта в зависимости от его состояния. Мы можем запрограммировать переход между состояниями, а затем определить отдельные состояния.

Чтобы лучше объяснить концепцию, мы расширим наш пример запроса на отпуск, чтобы реализовать конечный автомат.

4. Перечисления как конечные автоматы

Мы сосредоточимся на реализации enum конечных автоматов в Java. Возможны и другие реализации , и мы сравним их в следующем разделе.

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

public enum LeaveRequestState {

Submitted {
@Override
public LeaveRequestState nextState() {
return Escalated;
}

@Override
public String responsiblePerson() {
return "Employee";
}
},
Escalated {
@Override
public LeaveRequestState nextState() {
return Approved;
}

@Override
public String responsiblePerson() {
return "Team Leader";
}
},
Approved {
@Override
public LeaveRequestState nextState() {
return this;
}

@Override
public String responsiblePerson() {
return "Department Manager";
}
};

public abstract LeaveRequestState nextState();
public abstract String responsiblePerson();
}

В этом примере переходы конечного автомата реализованы с использованием абстрактных методов перечисления . Точнее, используя nextState() для каждой константы перечисления, мы указываем переход в следующее состояние. При необходимости мы также можем реализовать метод previousState() .

Ниже приведен тест для проверки нашей реализации:

LeaveRequestState state = LeaveRequestState.Submitted;

state = state.nextState();
assertEquals(LeaveRequestState.Escalated, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);

Запускаем запрос на отпуск в начальном состоянии Submitted . Затем мы проверяем переходы между состояниями, используя метод nextState() , который мы реализовали выше.

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

5. Преимущества реализации конечных автоматов с перечислениями Java

Реализация конечных автоматов с интерфейсами и классами реализации может потребовать значительного объема кода для разработки и поддержки.

Поскольку перечисление Java в своей простейшей форме представляет собой список констант, мы можем использовать перечисление для определения наших состояний. А поскольку перечисление также может содержать поведение, мы можем использовать методы для обеспечения реализации перехода между состояниями.

Наличие всей логики в простом перечислении позволяет получить чистое и прямое решение.

6. Заключение

В этой статье мы рассмотрели конечные автоматы и то, как их можно реализовать в Java с помощью Enums. Мы привели пример и протестировали его.

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

Как всегда, все фрагменты кода, упомянутые в этой статье, можно найти в нашем репозитории GitHub .