Библиотека мобильного разработчика | Android, iOS, Swift, Retrofit, Moshi, Chuck
9.59K subscribers
1.65K photos
81 videos
52 files
4.47K links
Все самое полезное для мобильного разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/b60af5a4

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a4adec1b17b35b6c0d8389
Download Telegram
🎮 Почему ваша ViewModel технически нестабильна и почему Compose не возражает

А вы знали, что почти все ViewModels нестабильны?

Когда мы только изучаем Compose,
нас учат использовать стабильный класс, а не нестабильный.

Но ViewModels нестабильны. Так почему же никто ничего не говорит о том, что мы используем нестабильные ViewModels?

🔹 Как Compose определяет стабильность?

Компилятор Compose считает класс стабильным, если:

🔘 Все его свойства (val/var) неизменяемы (val).

🔘 Или их тип «отслеживается» рантаймом Compose (например, MutableState<T>).

Взгляните на примеры:

// Стабильный класс
data class Stable(
val a: Int, // Стабильно
val b: MutableState<Int>, // Стабильно, отслеживается Compose
val c: SnapshotStateList<Int> // Стабильно, отслеживается Compose
)

// Нестабильный класс
data class Unstable(
var b: Int // Нестабильно из-за `var`
)

// "Неопределенная" стабильность
data class Runtime(
val i: Interface // Компилятор не знает, какая реализация будет на runtime.
)


Но есть важный нюанс: это правило работает только внутри модуля, где подключен компилятор Compose.

🔹 Что происходит на границах модулей?

Допустим, вы создали стабильную data class в слое данных (data) и внедрили её в ViewModel в слое презентации.

Логично ожидать, что ViewModel тоже будет стабильным. Но на практике — нет!

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

А раз наш ViewModel зависит от репозиториев и UseCase из других модулей (domain/data), то и он сам автоматически становится нестабильным.

🔹 Так почему же нестабильный ViewModel — это норма?

Ответ простой и лежит на поверхности: мы не передаем сам ViewModel в дочерние композаблы.

Вместо этого мы:

1. Создаем ViewModel один раз наверху (например, в NavGraph).

2. Коллектим его состояние (state), которое уже является стабильным.

3. Пробрасываем это стабильное состояние вниз по дереву композиции.

@Composable
fun Screen(viewModel: TestViewModel) { // ViewModel нестабилен, и это ок
val state by viewModel.state.collectAsState() // Состояние - стабильно

Child(state) // Передаем стабильный state
}

@Composable
fun Child(state: TestState) { // Стабильный пропс -> рекомпозиции оптимизированы
Text(state.data)
}


Compose-рантайм следит за изменениями в state. Сам ViewModel как объект не «пробрасывается» глубже и не триггерит лишних рекомпозиций.

🔹 Итоги

🔘 Для монолитных проектов: Это не проблема, компилятор видит все классы и корректно определяет стабильность.

🔘 Для многомодульных проектов: ViewModel почти всегда будет нестабильным из-за зависимостей из других модулей. И это нормально и безопасно, если вы передаете в композаблы не его самого, а его состояние.

Так что можете спать спокойно — с вашим кодом всё в порядке.

🐸 Библиотека мобильного разработчика

#АрхитектурныйКод #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤‍🔥1🔥1🤔1
Что такое абстрактные классы

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

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

abstract class Tree {
abstract val name: String
abstract val description: String
abstract fun info()
}


Каждый наследник обязан переопределять их все.

class Pine : Tree() {
override val name = "Сосна"
override val description = "Хвойное дерево с длинными иглами и округлыми шишками"
override fun info() = "$name - ${description.toLowerCase()}."
}


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

abstract class Tree {
abstract val name: String
abstract val description: String
fun info(): String = "$name - ${description.toLowerCase()}."
}

...

class Pine : Tree() {
override val name = "Сосна"
override val description = "Хвойное дерево с длинными иглами и округлыми шишками"
}

...

val pine = Pine()
println(pine.info())


Так как этот компонент класса уже не будет абстрактным, наследники не смогут его переопределить.

class Pine : Tree() {
override val name = "Сосна"
override val description = "Хвойное дерево с длинными иглами и округлыми шишками"

// ошибка: функция "info" является "final" и не может быть переопределена
override fun info() = description
}


Чтобы это исправить, нужно явно задать модификатор open для функции с конкретной реализацией. Тогда у наследников появляется выбор: либо не переопределять функцию и использовать реализацию суперкласса, либо переопределить и указать свою собственную реализацию.

abstract class Tree {
abstract val name: String
abstract val description: String

open fun info(): String = "$name - ${description.toLowerCase()}."
}


У абстрактного класса может быть конструктор.

abstract class Tree(val name: String, val description: String) {
open fun info(): String = "$name - ${description.toLowerCase()}."
}


Тогда каждый наследник должен предоставить для него значения.

class Pine(name: String, description: String) : Tree(name, description)

...

val pine = Pine("Сосна", "Хвойное дерево с длинными иглами и округлыми шишками")
println(pine.info())


🔹 Курс «Основы IT для непрограммистов»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

🐸 Библиотека мобильного разработчика

#буст #JuniorKit #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM