Комментарии 5
"Охрененно" круто, но только почему-то во всех приличных книжках используют термин "конечный автомат", а не "“Finite-state machine”? Я имею в виду книжки на "русском" языке, даже если они и переведены с иноземного!
Стейт-машина – это полезный паттерн, который поможет вам запрограммировать множество состояний и переходов между ними когда это необходимо.
Тут нужно оговорить что сам паттерн State в статье не рассмотрен. Потому что никакого отношения к enum'ам он не имеет. Наоборот, это уход от enum'ов к ООП.
Паттерн state на Swift обычно реализуют примерно так:
1. Формируется список методов которые описывают внешние действия
protocol State {
func startCall() -> State
func endCall() -> State
}
2. Реализуется возможные состояния:class InactiveState: State {
func startCall() -> State {
print("Call is active")
return StartedState()
}
func endCall() -> State {
print("Error: can't end the call, call is already inactive")
return self
}
}
class ActiveState: State {
func startCall() -> State {
print("Error: can't start the call, call is already active")
return self
}
func endCall() -> State {
print("Call is active")
return InactiveState()
}
}
3. Далее класс state используется в следующем виде:class StateUser {
private var state: State = InitialState()
func makeCall() {
self.state = state.startCall()
self.state = state.endCall()
}
}
В чем плюсы паттерна:
Количество состояний не ограниченно - можно добавлять/убирать новые не меняя код у пользователя - если количество действий не меняется.
Обработка конкретного состояния ограничено конкретным классов
Список воздействия и состояния развязаны. Список воздействий определяются списком методов State'а, а список состояний - списком классов.
Я активно занимался внедрением Finite-State Machine у себя на работе и безусловно это одно из самых лучших паттернов в мобильной разработке. Большая часть работы приложения это реакция на события, это может быть состояние об Интернет соединении, запуск приложения, аутентификация пользователя и т.п. В результате бизнес логика может распределена по зоне ответственности более детально. В тоже самое время состояния очень легко тестировать, потому как они отвечают только за переход от текущего состояние в некое следующее по заданным правилам.
В целом состояние можно описать следующим образом:
Тип состояния содержит список состояний, список событий и функцию редуктера (reducer), которая мутирует состояние.
Сама по себе машина состояния это просто менеджер или тот кто владеет состоянием и может его изменить через редуктор. Здесь важно понимать что извне состояние нельзя изменить в обход машины состояния. Можно только прочитать текущее значение или подписаться не его изменение.
Для реализации данного подхода вам необходимо определить протокол состояния который может выглядеть следующий образом:
protocol StateType: Equatable {
associatedtype Event
associatedtype Command = Void
/// The initial state.
static var initial: Self { get }
/// Updates the current state to a new state caused by a given event.
@discardableResult
mutating func reduce(with event: Event) -> Command
}
Command
по умолчанию может никак не использовать машиной состояния, но может быть очень полезен для дополнительных действий со стороны машины состояния. В некоторых случаешь состояние может перейти в такой случай где менеджеру (машине состояния) необходимо подсказка как интерпретировать одно и тоже состояние для разных возможных действий со стороны менеджера.
Ну и последнее приведу более менее близкий к реальности пример состояния: https://gist.github.com/buh/63e1dd41b267bb65baacf03b8557786a
В реальном приложении состояние аутентификации имеет больше событий и состояний переходов, но главное, что при переходи на "чистую" машину состояний целостность, изоляция бизнес логики и отказоустойчивость повысилась значительно.
Стейт-машины в iOS