0Pricing
Kotlin Academy · Урок

Практические шаблоны

Декораторы с делегированием

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

Шаблон декоратора

Декоратор оборачивает объект, чтобы добавить поведение, сохраняя тот же интерфейс. Ключевое слово by в Kotlin делает декораторы очень простыми: переадресуйте все вызовы, а затем переопределите нужные части.

Базовый компонент

Начнём с простого источника данных. Декораторы будут оборачивать его.

interface DataSource {
    fun fetch(): String
}

class NetworkSource : DataSource {
    override fun fetch() = "raw-data"
}

fun main() {
    println(NetworkSource().fetch())
}

Декоратор журналирования

Декоратор делегирует работу обёрнутому источнику и добавляет журналирование вокруг интересующего его вызова.

interface DataSource { fun fetch(): String }
class NetworkSource : DataSource { override fun fetch() = "raw-data" }

class LoggingSource(private val inner: DataSource) : DataSource by inner {
    override fun fetch(): String {
        println("fetching...")
        val result = inner.fetch()
        println("done")
        return result
    }
}

fun main() {
    println(LoggingSource(NetworkSource()).fetch())
}

Декоратор кэширования

Другой декоратор кэширует первый результат, поэтому повторные вызовы пропускают базовый источник.

interface DataSource { fun fetch(): String }
class NetworkSource : DataSource { override fun fetch() = "raw-data" }

class CachingSource(private val inner: DataSource) : DataSource by inner {
    private var cached: String? = null
    override fun fetch(): String {
        return cached ?: inner.fetch().also { cached = it }
    }
}

fun main() {
    val s = CachingSource(NetworkSource())
    println(s.fetch())
    println(s.fetch()) // served from cache
}

Объединение декораторов

Поскольку каждый декоратор сам является DataSource, их можно объединять в цепочку. Порядок имеет значение: внешний декоратор оборачивает внутренний.

interface DataSource { fun fetch(): String }
class NetworkSource : DataSource { override fun fetch() = "raw-data" }

class LoggingSource(private val inner: DataSource) : DataSource by inner {
    override fun fetch(): String { println("log"); return inner.fetch() }
}
class CachingSource(private val inner: DataSource) : DataSource by inner {
    private var c: String? = null
    override fun fetch() = c ?: inner.fetch().also { c = it }
}

fun main() {
    val s = LoggingSource(CachingSource(NetworkSource()))
    println(s.fetch())
    println(s.fetch())
}

Декоратор проверки

Декораторы также могут защищать входные данные. Здесь декоратор выполняет проверку перед делегированием настоящему исполнителю.

interface Processor { fun process(input: String): String }
class UpperProcessor : Processor { override fun process(input: String) = input.uppercase() }

class ValidatingProcessor(private val inner: Processor) : Processor by inner {
    override fun process(input: String): String {
        require(input.isNotBlank()) { "empty input" }
        return inner.process(input)
    }
}

fun main() {
    println(ValidatingProcessor(UpperProcessor()).process("hello"))
}

Обёртка коллекции только для чтения

Делегирование упрощает создание обёрток-адаптеров. Здесь мы оборачиваем список, добавляем собственный метод сводки и переадресуем все операции списка.

class SummarizedList(private val inner: List<Int>) : List<Int> by inner {
    fun total() = inner.sum()
}

fun main() {
    val list = SummarizedList(listOf(1, 2, 3))
    println(list.size)
    println(list.total())
}

Подсчёт вызовов

Декоратор может прозрачно отслеживать показатели, что удобно для мониторинга без изменения настоящей реализации.

interface Service { fun call(): String }
class RealService : Service { override fun call() = "ok" }

class CountingService(private val inner: Service) : Service by inner {
    var calls = 0
        private set
    override fun call(): String {
        calls++
        return inner.call()
    }
}

fun main() {
    val s = CountingService(RealService())
    s.call(); s.call()
    println(s.calls)
}

Почему здесь выигрывает делегирование

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

Контрольный список шаблона

Чтобы создать декоратор с делегированием:

  • Примите обёрнутый объект как private val
  • Делегируйте интерфейс с помощью by
  • Переопределяйте только члены, поведение которых Вы улучшаете
  • Вызывайте внутренний объект внутри переопределения

Объединяем всё вместе

Декораторы, адаптеры и прокси естественным образом создаются с помощью делегирования классов. В сочетании с небольшими интерфейсами они позволяют создавать гибкие многослойные системы, которые легко тестировать и расширять.

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

Проверьте, насколько хорошо Вы понимаете практические шаблоны делегирования.

Итоги

Вы создали практические декораторы с помощью делегирования:

  • Обёртки для журналирования, кэширования, проверки и подсчёта
  • Объединение декораторов, поскольку каждый реализует один и тот же интерфейс
  • Переопределение только улучшенных членов и переадресация остальных

На этом курс делегирования классов завершён.

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

Урок «Практические шаблоны» бесплатный?

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

Чему я научусь в уроке «Практические шаблоны»?

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

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

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

Сколько времени занимает урок «Практические шаблоны»?

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

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

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

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

  1. Ключевое слово by
  2. Делегирование членам
  3. Делегирование и наследование
  4. Практические шаблоны
← Назад к Kotlin Academy