Шаблон Strategy
Часть раздела Объектно-ориентированное программирование путешествия по Swift на Coddy. Урок 45 из 57.
Паттерн стратегия помещает каждый вариант алгоритма в собственный тип, реализующий общий протокол. Код, который его использует, хранит стратегию и может заменять её во время выполнения:
protocol ShippingStrategy {
func cost(weight: Int) -> Int
}
struct Standard: ShippingStrategy { func cost(weight: Int) -> Int { 5 + weight } }
struct Express: ShippingStrategy { func cost(weight: Int) -> Int { 15 + weight * 2 } }
struct Checkout {
var shipping: any ShippingStrategy
func total(price: Int, weight: Int) -> Int { price + shipping.cost(weight: weight) }
}
var c = Checkout(shipping: Standard())
print(c.total(price: 100, weight: 3))
c.shipping = Express()
print(c.total(price: 100, weight: 3))Вывод:
108
121В Swift замыкания часто достаточно для стратегии с одним методом. Тип хранит функцию и вызывает её:
struct Sorter {
var rule: (String, String) -> Bool
func sort(_ words: [String]) -> [String] { words.sorted(by: rule) }
}
let words = ["banana", "kiwi", "apple"]
var s = Sorter(rule: <)
print(s.sort(words))
s.rule = { $0.count < $1.count }
print(s.sort(words))Результат:
["apple", "banana", "kiwi"]
["kiwi", "apple", "banana"]Стратегия заменяет switch внутри алгоритма. Новые варианты становятся новыми типами (или замыканиями), а код, который их использует, остаётся неизменным:
// без стратегии: каждая новая опция изменяет эту функцию
func cost(kind: String, weight: Int) -> Int {
switch kind { case "standard": return 5 + weight; default: return 15 + weight * 2 }
}
// со стратегией: Checkout никогда не изменяется
struct Overnight: ShippingStrategy { func cost(weight: Int) -> Int { 40 } }Стратегии можно выбирать на основе данных, например настройки пользователя, с помощью небольшой фабрики:
protocol Discount { func apply(_ price: Int) -> Int }
struct NoDiscount: Discount { func apply(_ p: Int) -> Int { p } }
struct Percent: Discount { let rate: Int; func apply(_ p: Int) -> Int { p - p * rate / 100 } }
func discount(for member: String) -> any Discount {
member == "gold" ? Percent(rate: 20) : NoDiscount()
}
print(discount(for: "gold").apply(200))
print(discount(for: "basic").apply(200))Вывод:
160
200Задание
ЛегкоАтака игрового персонажа — это стратегия. Протокол Attack содержит name и damage(strength:) -> Int. Реализуйте Sword (strength * 2), Bow (strength + 5) и Magic (сохраняемое значение mana; урон равен strength * 3, пока mana не меньше 10, и 1 в противном случае; сам damage не изменяет mana). Hero хранит свои strength и attack, а strike() возвращает Ada hits with sword for 20.
Предоставленный код считывает имя и силу героя, затем команды equip sword, equip bow, equip magic 15 или strike. Он выводит результат каждого удара.
Ваш код должен находиться в файлах Attack.swift, Sword.swift, Bow.swift, Magic.swift и Hero.swift. В main.swift содержится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
// Предоставленный код ввода/вывода: оставьте его как есть
var input: [String] = []
while let line = readLine() { input.append(line) }
let head = input[0].split(separator: ",").map(String.init)
var hero = Hero(name: head[0], strength: Int(head[1])!, attack: Sword())
for cmd in input.dropFirst() {
let p = cmd.split(separator: " ").map(String.init)
if p[0] == "strike" { print(hero.strike()); continue }
switch p[1] {
case "bow": hero.attack = Bow()
case "magic": hero.attack = Magic(mana: Int(p[2])!)
default: hero.attack = Sword()
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовМетоды и selfИнициализаторыКлассы — это ссылкиПовторение — читательский билет4Инкапсуляция
Контроль доступаТолько чтение извнеЗащита состоянияИнициализаторы с возможностью сбояПовторение — банковский счёт7Полиморфизм и обобщения
ПолиморфизмПриведение типовОбобщённые функцииОбобщённые типыПовторение — калькулятор фигур10Паттерны проектирования. Часть 2
Паттерн делегированияПаттерн декораторПаттерн строительПаттерн состояния2Структуры и типы-значения
СтруктурыСемантика значенийИзменяющие методыСтруктура или классПовторение — элемент инвентаря5Наследование
ПодклассыПереопределение методовИнициализаторы и superfinal и иерархииПовторение — иерархия сотрудников8Стандартные протоколы
CustomStringConvertibleEquatableComparableHashableПовторение — пользовательская сортировка11Проект: Управление библиотекой
Книги и читателиВыдача книгПотренируйтесь самостоятельно: Песочница Swift