← Назад к вопросам

Есть ли стейт-машина в MVI

2.0 Middle🔥 82 комментариев
#Архитектура и паттерны

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

📐 Архитектурный паттерн MVI и концепция стейт-машины

Да, MVI напрямую реализует концепцию стейт-машины (state machine), и это его фундаментальный принцип.

В MVI экран (или компонент) рассматривается как детерминированная машина состояний, где:

  • Вход (Input) — это Intents (намерения пользователя или системы).
  • Состояние (State) — это иммутабельный (неизменяемый) объект State, полностью описывающий всё, что нужно для отображения UI.
  • Выход (Output) — это само UI, которое является чистой функцией от State.

Ключевые аспекты стейт-машины в MVI

1. Единственный источник истины (Single Source of Truth)

Весь UI управляется одним иммутабельным объектом State. Это состояние можно представить как конечный автомат, где каждое возможное отображение кнопки, текста, списка и т.д. — это конкретная комбинация значений внутри этого объекта.

// Пример состояния экрана загрузки данных
data class NewsListState(
    val items: List<NewsItem> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null,
    val isRefreshing: Boolean = false
)

2. Детерминированность и чистота

Новое состояние вычисляется детерминировано на основе предыдущего состояния и полученного намерения (Intent). Эта логика содержится в редьюсере (Reducer) — чистой функции без побочных эффектов.

// Пример редьюсера (используя псевдо-котлин с корутинами/Flow)
fun newsReducer(state: NewsListState, intent: NewsIntent): NewsListState {
    return when (intent) {
        is NewsIntent.LoadStarted -> state.copy(isLoading = true, error = null)
        is NewsIntent.LoadSuccess -> state.copy(isLoading = false, items = intent.items)
        is NewsIntent.LoadError -> state.copy(isLoading = false, error = intent.message)
        is NewsIntent.RefreshStarted -> state.copy(isRefreshing = true)
        is NewsIntent.UserSelectedItem -> state.copy(selectedItemId = intent.id)
    }
}

3. Цикл данных (Unidirectional Data Flow)

MVI жёстко определяет однонаправленный поток данных, который является классической моделью для стейт-машин:

graph LR
    A[UI] -->|Порождает Intent| B(ViewModel/Presenter)
    B -->|Отправляет Intent| C[Reducer / Processor]
    C -->|Вычисляет новое State| D[State Holder]
    D -->|Эмитит новое State| A

Алгоритм цикла:

  1. Пользовательское действие (тап, свайп) или системное событие (приём push) порождает Intent.
  2. Intent направляется в обработчик (ViewModel).
  3. На основе текущего State и полученного Intent Reducer вычисляет новое State.
  4. Новый State отправляется обратно в UI.
  5. UI полностью перерисовывается (реактивно) в соответствии с новым состоянием.

4. Предсказуемость и отладка

Поскольку состояние иммутабельно и все переходы происходят через явные Intents, всю историю изменений можно легко логировать или даже сохранить (например, для воспроизведения багов). Это прямое следствие архитектуры стейт-машины.


Отличие от других паттернов (MVP, MVVM)

  • В MVP/MVVM состояние часто распылено: часть во View, часть в Presenter/ViewModel, часть в модели. Нет единого, управляемого объекта.
  • В MVI состояние централизовано, явно и неизменно. Это делает MVI более строгим и предсказуемым, но иногда и более вербозным (требует написания большего количества кода для описания всех состояний и намерений).

Пример жизненного цикла состояния в MVI

Представьте экран с данными. Его состояния в MVI будут явно описаны:

  1. State(isLoading = true, items = [], error = null)Состояние загрузки.
  2. State(isLoading = false, items = [...], error = null)Состояние успеха.
  3. State(isLoading = false, items = [], error = "Нет сети")Состояние ошибки.

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

Итог: MVI не просто "похож" на стейт-машину — он является её прямой и строгой имплементацией для построения UI. Это его главная сила (предсказуемость, тестируемость) и главная сложность (необходимость явно моделировать все возможные состояния экрана).