Strategy, Decorator, Adapter и Command
Узнаём задачу паттерна и ловушки делегирования
Выберите изменяемую часть
Strategy заменяет алгоритм под общим договором. Decorator оборачивает тот же договор дополнительным поведением. Adapter переводит чужой интерфейс в нужный приложению, а Facade даёт удобный вход в несколько операций подсистемы. Command представляет действие данными/объектом; возможность undo требует отдельного обратного действия или снимка.
Это классификация для разбора исходных примеров из draft/patterns.md: сама форма «класс содержит другой класс» не определяет паттерн. Проверяйте, что именно меняется и какой договор видит клиент.
Названия взяты из каталога Gamma, Helm, Johnson и Vlissides — Design Patterns. Издатель также предоставляет авторский фрагмент об Adapter: его назначение — согласование интерфейсов. Примеры на Kotlin ниже составлены для этой темы.
Маленькая стратегия
fun interface Shipping { fun cost(weight: Int): Int }
class Checkout(private val shipping: Shipping) {
fun delivery(weight: Int): Int = shipping.cost(weight)
}
val freeDelivery = Checkout(Shipping { 0 })
Для одного метода Kotlin fun interface допускает SAM-преобразование из лямбды. Множество наследников не обязательно. Функциональные интерфейсы Kotlin.
by не меняет внутренние вызовы делегата
interface Reporter {
fun message(): String
fun report(): String
}
class PlainReporter : Reporter {
override fun message() = "plain"
override fun report() = message()
}
class Loud(private val base: Reporter) : Reporter by base {
override fun message() = "LOUD"
}
Loud(PlainReporter()).report() вернёт plain: делегированный report выполняется на base, его вызов message не перенаправляется в обёртку. Если нужна другая семантика, явно переопределите report. Делегирование Kotlin.
Порядок обёрток — часть поведения
В нашем примере Logging(Retry(client)) пишет один лог на всю операцию, а Retry(Logging(client)) — лог каждой попытки, если Logging логирует каждый вызов. Для кеша и авторизации порядок тоже требует договорённости. Паттерн не освобождает от проверки ошибок и побочных эффектов.
DI позволяет передать выбранную комбинацию снаружи и подменить её в тесте. Dependency injection.
Основа отбора: draft/patterns.md, draft/Architecture interview.md, вопросы ООП из draft/LINKS.txt.