Зачем нужен?
Паттерн Strategy применяется для уменьшения связанности. В двух словах: программируйте на уровне интерфейсов, а не реализаций.
Соответствует open/closed принципу SOLID: изменение конкретных классов не влияет на клиента, т.к он зависит только от интерфейса.
Определение
- определяет семейство алгоритмов(поведений), инкапсулирует и обеспечивает их взаимозаменяемость. Позволяет модифицировать алгоритмы независимо от их использования на стороне клиента.
- инкапсулируем абстракцию в интерфейсе, скрываем реализацию в конкретных классах
Реализация
- Найдите алгоритм(поведение)
- Определите сигнатуру в супертипе(интерфейсе/абстрактном классе/протоколе)
- Скройте реализацию(или реализации) в конкретном классе, который соответствует этому интерфейсу
- Свяжите клиента алгоритма с интерфейсом
Пример
Допустим у нас есть утки. Утки могут крякать по разному: кряканье, писк, а могут и вовсе не крякать. Алгоритмом в данном случае будет способ кряканья.
Теперь определим сигнатуру:
// в качестве супертипа - протокол(интерфейс)
protocol QuackBehavior {
func quack()
}
Скроем реализацию в конкретных классах:
// конкретные классы поведения, описываемые супертипом
class Quack: QuackBehavior {
func quack() {
print("Я крякаю: quack-quack")
}
}
class QuackMute: QuackBehavior {
func quack() {
print("Я не крякаю :(")
}
}
class Squeak: QuackBehavior {
func quack() {
print("Я пищу: squeak-squeak")
}
}
Свяжем клиента(в нашем случае - утку) с интерфейсом:
class Duck {
// Клиент Duck связан с абстракцией, а не с конкретными реализациями
private var quackBehavior: QuackBehavior
required convenience init() {
self.init(quackBehavior: QuackMute())
}
private init(quackBehavior: QuackBehavior) {
self.quackBehavior = quackBehavior
}
func performQuack() {
// делегируем поведение
quackBehavior.quack()
}
func setQuackBehavior(quackBehavior: QuackBehavior) {
self.quackBehavior = quackBehavior
}
}
В данной реализации метод setQuackBehavior позволяет динамически изменять поведение.
В качестве другого примера можно рассмотреть магазин, который осуществляет доставку разными способами: самолетом, самолетом-экспресс, морем. В данном случае алгоритм - способ доставки. Конкретные классы перечислены выше. Изменения методов доставки(например, квадрокоптером) никак не повлияют на клиента.
Стоить заметить, что вместо создания конкретных классов можно применить протоколо-ориентированный подход, а точнее использование миксинов. Т.е создаем миксины(содержащие поведение), с помощью расширений протокола добавляем реализацию по умолчанию, далее конфигурируем конкретные классы с необходимым набором поведений. В результате дублирования кода не будет.
Подробнее про протоколо-ориентированный подход:
- Знакомство с протоколо-ориентированным программированием в Swift 2
- Introducing Protocol-Oriented Programming in Swift 2
- Protocol-Oriented Programming in Swift 2
- Mixins over Inheritance