0Pricing
Kotlin Academy · Урок

Моделирование состояния интерфейса с помощью запечатанных классов

Применяйте запечатанные классы для представления состояний интерфейса Loading, Success и Error.

«Моделирование состояния интерфейса с помощью запечатанных классов» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Проблема состояния UI

У экранов UI бывает несколько состояний: загрузка, отображение данных, отображение ошибки или пустое состояние. Управлять ими с помощью логических флагов (isLoading, hasError) ненадёжно. Запечатанные классы решают эту проблему.

Классический логический ад

Использование отдельных логических флагов приводит к невозможным состояниям.
// Fragile: isLoading=true AND hasError=true is invalid
var isLoading = false
var hasError = false
var data: List<Item>? = null
// 8 combinations, most of which are invalid!

Запечатанное состояние: только допустимые состояния

Запечатанный класс делает недопустимые состояния непредставимыми.
sealed class UiState<out T>
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val message: String, val retryable: Boolean = true) : UiState<Nothing>()
object Empty : UiState<Nothing>()

Отрисовка на основе состояния

Используйте when, чтобы отрисовать правильный UI для каждого состояния.
fun render(state: UiState<List<Item>>) = when (state) {
    Loading -> showProgressBar()
    is Success -> showItems(state.data)
    is Error -> showError(state.message, state.retryable)
    Empty -> showEmptyView()
}

ViewModel выдаёт состояния

ViewModel выдаёт запечатанные состояния через StateFlow.
class ItemViewModel : ViewModel() {
    private val _state = MutableStateFlow<UiState<List<Item>>>(Loading)
    val state: StateFlow<UiState<List<Item>>> = _state

    fun loadItems() = viewModelScope.launch {
        _state.value = Loading
        _state.value = try {
            Success(repository.getItems())
        } catch (e: Exception) {
            Error(e.message ?: "Unknown error")
        }
    }
}

Составные состояния с содержимым внутри

Используйте обобщённые запечатанные состояния для работы с любым типом данных.
fun <T> loading(): UiState<T> = Loading
fun <T> success(data: T): UiState<T> = Success(data)
fun <T> error(msg: String): UiState<T> = Error(msg)

Пример состояния постраничной загрузки

Запечатанные классы естественным образом моделируют состояния списков с постраничной загрузкой.
sealed class PageState
object Idle : PageState()
object FirstLoad : PageState()
data class Content(val items: List<Item>, val hasMore: Boolean) : PageState()
data class LoadingMore(val items: List<Item>) : PageState()
data class Error(val items: List<Item>?, val msg: String) : PageState()

Преобразование состояния

Преобразуйте успешные состояния, не затрагивая остальные варианты.
fun <T, R> UiState<T>.mapData(transform: (T) -> R): UiState<R> = when (this) {
    is Success -> Success(transform(data))
    is Error -> this
    Loading -> Loading
    Empty -> Empty
}

Тестирование с запечатанными состояниями

Состояния легко тестировать — достаточно проверить, какой вариант был выдан.
@Test
fun testLoadingState() {
    viewModel.loadItems()
    assertEquals(Loading, viewModel.state.value)
}
@Test
fun testSuccessState() {
    viewModel.setData(listOf(item))
    assert(viewModel.state.value is Success)
}

Общее состояние против нескольких потоков

Один поток запечатанного состояния понятнее, чем несколько отдельных потоков, которые должны оставаться синхронизированными.
// Fragile: two flows must stay in sync
val loading = MutableStateFlow(false)
val error = MutableStateFlow<String?>(null)

// Clean: one sealed state
val state = MutableStateFlow<UiState<List<Item>>>(Loading)

Добавление метаданных в состояния

Запечатанные подтипы могут содержать соответствующие метаданные.
data class Error(
    val message: String,
    val code: Int = 0,
    val retryable: Boolean = true,
    val timestamp: Long = System.currentTimeMillis()
) : UiState<Nothing>()

Быстрая проверка

Какую ключевую проблему решает моделирование состояния UI с помощью запечатанных классов?

Итоги

Моделируйте состояние UI с помощью запечатанных классов: Loading, Success(data), Error(message), Empty. Используйте when для отрисовки каждого варианта. Один StateFlow с запечатанным типом лучше нескольких логических флагов. Далее: вложенные запечатанные иерархии!

Часто задаваемые вопросы

Урок «Моделирование состояния интерфейса с помощью запечатанных классов» бесплатный?

Да — полный текст урока «Моделирование состояния интерфейса с помощью запечатанных классов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Моделирование состояния интерфейса с помощью запечатанных классов»?

Применяйте запечатанные классы для представления состояний интерфейса Loading, Success и Error. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

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

Сколько времени занимает урок «Моделирование состояния интерфейса с помощью запечатанных классов»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

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

Все уроки этого курса

  1. sealed class и sealed interface: когда что использовать
  2. Полное when с запечатанными иерархиями
  3. Моделирование состояния интерфейса с помощью запечатанных классов
  4. Вложенные и объединённые запечатанные иерархии
← Назад к Kotlin Academy