🔮 Убийца бойлерплейта: Встречайте Context Parameters (Kotlin 2.x)
Мы так привыкли к Dependency Injection (Dagger/Hilt/Koin), что перестали замечать, как он раздувает наш код.
Сценарий из жизни: вам нужно залогировать событие глубоко в слое бизнес-логики. Вы берете интерфейс Logger и прокидываете его через конструкторы пяти промежуточных классов, хотя он нужен только в одном методе на самом дне.
В Kotlin 2.1+ (KEEP-367) активно допиливают киллер-фичу, которая меняет правила игры - Context Parameters.
Историческая справка: Если вы помните экспериментальные Context Receivers (синтаксис context(Logger)), забудьте их. JetBrains официально задепрекейтили их в версии 2.0.20, заменив на более строгие и читаемые Context Parameters.
Как это выглядит теперь?
Допустим, у нас есть TransactionScope и Logger. Мы не хотим инжектить их в классы-прослойки, мы хотим потребовать их наличие в функции неявно.
// Новая магия: context(...) перед функцией с явными именами переменных
context(logger: Logger, tx: TransactionScope)
fun deleteUser(userId: String) {
logger.log("Deleting user $userId")
tx.execute {
// ... логика удаления в БД
}
}
В чем отличие от обычных параметров?
При вызове функции deleteUser вам не нужно передавать эти параметры руками! Компилятор сам найдет их в текущем скоупе (scope) и подставит под капотом.
class UserUseCase(
private val logger: Logger,
private val tx: TransactionScope
) {
fun execute() {
// Вводим зависимости в область видимости
with(logger) {
with(tx) {
// Вызываем функцию БЕЗ передачи логгера и транзакции!
// Компилятор сам слинкует их из контекста.
deleteUser("user_123")
}
}
}
}
(На практике вы настроите скоуп один раз на самом верхнем уровне — например, в базовой ViewModel или HTTP-хендлере Ktor — и вся вложенная цепочка вызовов получит к ним доступ).
Почему это круто для архитектуры?
1. Чистые сигнатуры: Бизнес-функции принимают только бизнес-аргументы (например, userId: String). Вся техническая инфраструктура (логирование, аналитика, права доступа, транзакции) выносится за скобки.
2. Читаемость (в отличие от старых Receivers): Раньше методы из контекста вызывались неявно (просто log()), и в большом классе было невозможно понять, откуда пришел этот метод. Теперь у нас есть жесткое имя: logger.log().
3. Идеально для Cross-cutting concerns: Это элегантное решение для сквозного функционала, который не должен загрязнять доменную модель.
⚠️ Правило Сеньора:
Context Parameters - это не замена конструкторному DI.
Если классу нужен UserRepository для выполнения всех его основных задач, передавайте его в конструктор. Используйте context(...) только для тех сущностей, которые меняются в зависимости от среды выполнения (call chain), или для утилитарных инструментов (например, AnalyticsScope).
Просмотры: 76