Паттерн «Стратегия»
Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 45 из 57.
Паттерн стратегия помещает каждый вариант алгоритма в отдельный класс с общим интерфейсом. Код, который его использует, хранит одну стратегию и может заменить её во время выполнения программы:
interface Shipping {
fun cost(kg: Int): Int
}
class Standard : Shipping {
override fun cost(kg: Int) = 5 + kg
}
class Express : Shipping {
override fun cost(kg: Int) = 15 + kg * 2
}
class Checkout(var shipping: Shipping) {
fun total(price: Int, kg: Int) = price + shipping.cost(kg)
}Внутри main:
val checkout = Checkout(Standard())
println(checkout.total(100, 3))
checkout.shipping = Express()
println(checkout.total(100, 3))Вывод:
108
121Когда у стратегии есть единственный метод, достаточно типа функции. Класс хранит функцию и вызывает её:
class Formatter(var style: (String) -> String) {
fun show(words: List<String>) = words.joinToString(" ") { style(it) }
}Внутри main:
val f = Formatter { it.uppercase() }
println(f.show(listOf("hello", "world")))
f.style = { it.reversed() }
println(f.show(listOf("hello", "world")))Результат:
HELLO WORLD
olleh dlrowСтратегия заменяет when внутри алгоритма. При использовании when каждый новый вариант означает редактирование алгоритма. При использовании стратегий новый вариант — это новый класс или лямбда, а Checkout никогда не изменяется:
// без стратегии: каждый новый вариант правит эту функцию
fun cost(kind: String, kg: Int) = when (kind) {
"standard" -> 5 + kg
else -> 15 + kg * 2
}
// со стратегией: добавьте класс, оставьте Checkout в покое
class Overnight : Shipping {
override fun cost(kg: Int) = 40
}Стратегия часто выбирается на основе данных, например плана клиента, с помощью карты или небольшой фабрики:
val discounts: Map<String, (Int) -> Int> = mapOf(
"gold" to { price -> price * 80 / 100 },
"silver" to { price -> price * 90 / 100 }
)
fun finalPrice(plan: String, price: Int) = discounts[plan]?.invoke(price) ?: priceВнутри main:
println(finalPrice("gold", 200))
println(finalPrice("silver", 200))
println(finalPrice("basic", 200))Вывод:
160
180
200Задание
ЛегкоАтака героя — это стратегия. Интерфейс Attack (с name и damage(strength)) предоставлен. Напишите Sword (урон равен силе, умноженной на 2), Bow (сила плюс 5) и Magic(mana) (сила, умноженная на 3, если мана не меньше 10, иначе 1). Hero(name, strength, attack) может в любой момент изменить свой attack, а strike() возвращает Ada hits with sword for 20, не проверяя, какую атаку он использует.
Предоставленный код считывает name,strength, а затем команды equip bow, equip magic 15, equip sword или strike и выводит результат каждого удара.
Ваш код должен находиться в файлах Sword.kt, Bow.kt, Magic.kt, Hero.kt и Attack.kt. В Main.kt находится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
fun main() {
// Предоставленный код ввода/вывода: оставьте его как есть
val input = generateSequence(::readLine).toList()
val (name, strength) = input[0].split(",")
val hero = Hero(name, strength.toInt(), Sword())
for (cmd in input.drop(1)) {
val p = cmd.split(" ")
if (p[0] == "strike") {
println(hero.strike())
continue
}
hero.attack = when (p[1]) {
"bow" -> Bow()
"magic" -> Magic(p[2].toInt())
else -> Sword()
}
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовПервичные конструкторыБлоки init и валидацияВторичные конструкторыПовторение — читательский билет4Наследование
Открытые классы и подклассыПереопределение членовsuper и конструкторыАбстрактные классыПовторение — каталог сотрудников7Специальные классы
Классы данныхКлассы перечисленийЗапечатанные иерархииequals, hashCode, toStringПовторение — трекер заказов10Паттерны проектирования. Часть 2
Делегирование с помощью byПаттерн декораторПаттерн строительПаттерн состояние2Свойства и инкапсуляция
Пользовательские аксессорыМодификаторы видимостиПриватные сеттерыlazy и lateinitПовторение — банковский счёт5Интерфейсы
Объявление интерфейсовМетоды по умолчаниюМножественные интерфейсыИнтерфейсы как типыПовторение — способы оплаты8Обобщения
Обобщённые функцииОбобщённые классыОграничения типовВариативность с in и outПовторение — очередь с приоритетом11Проект: Управление библиотекой
Книги и читателиВыдача книг3Объекты и компаньоны
Объявления объектовОбъекты-компаньоныКонстанты с constОбъектные выраженияПовторение — билетная касса6Полиморфизм
Динамическая диспетчеризацияПроверка типов и умные приведения типовwhen с типамиФункции-расширенияПовторение — смотритель зоопарка9Паттерны проектирования. Часть 1
Паттерн «Одиночка»Фабричный паттернПаттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Песочница Kotlin