Как пишут в Википедии, стратегия — это поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости.
Вот так выглядит стратегия в UML
Непонятно ровным счетом ничего, скажут многие и будут правы. Я пытался подойти к этому шаблону с разных сторон, но так и не понял его, пока не пришел к тому, что я его уже использую в своем коде.
Постараюсь изложить свой ход мысли, возможно, он будет понятнее, чем сухие выдержки из сборников паттернов, которые все пытаются подать в том или ином виде при рассмотрении этого паттерна.
Что представляет собой паттерн стратегия
Итак, стратегия не что иное, как обычное делегирование с возможностью выбора конкретной реализации делегата с определенным интерфейсом из существующего набора. Поведение объекта делегируется другому объекту, который реализует это поведение. В итоге, делегат реализует поведение и является зависимостью для объекта, поведение которого он реализует.
Таким образом, выполняется правило единой ответственности класса (объект отвечает за набор данных, а реализация поведения — за то, как эти данные себя ведут), что очень хорошо сказывается на размере классов, количестве зависимостей, дублировании кода и дальнейшей поддержке проекта.
Т.к. все делегаты, реализующие поведение объекта, имеют один интерфейс, то не составляет труда обеспечить возможность реализации различного поведения для каждого из объектов посредством внедрения требуемой реализации поведения. Сделать это можно на этапе создания объекта, просто передав в конструктор требуемый экземпляр реализации поведения.
Зачем нужен паттерн стратегия
Все это, конечно, хорошо, но зачем же все это нужно. Ведь такая реализация приводит к тому, что мы усложняем статический анализ кода, пряча конкретную реализацию поведения за интерфейсом и используемым механизмом внедрения зависимостей.
Реализация паттерна стратегия позволяет более гибко использовать полиморфизм поведения объекта без необходимости дублирования кода и наращивания уровней иерархии наследования.
Чем стратегия лучше переопределения методов в наследниках
Можно заметить, что все преимущества паттерна стратегия можно реализовать обычным наследованием, если перекрывать в наследнике реализацию поведенческого метода родителя. Но наследование не позволяет получить ни поведение не из родителя ни различное поведение от двух разных родителей.
Всегда наступает такой момент, когда приходится дублировать код, чтобы не усложнять иерархию наследования ради перекрытия поведения родителя либо получения нужного поведения из нужного родителя.
Реализация паттерна стратегия лишена этого недостатка. Всегда можно применить любой набор существующих поведений и расширять его до бесконечности без необходимости каких-либо изменений структуры приложения.
Когда стоит применять паттерн стратегия
Не стоит рассматривать паттерн стратегия как обязанность. Если есть поведение, то должен быть интерфейс, его реализация, и эта реализация должна внедряться как зависимость. Ничего подобного. Нужно идти от простого к сложному, всегда оставляя место для шага вперед.
Начать можно с обычного делегирования, чтобы отделить поведение от данных и вынести реализацию поведения в отдельный объект. Если в будущем появится необходимость реализации нескольких вариантов поведения, можно реализовать интерфейс для поведения и перейти к паттерну стратегия.
Такой подход не требует больших усилий и существенных архитектурных изменений проекта.
Программная реализация паттерна стратегия
Реализацию на php приводить не буду, т.к. она довольно простая, если понять суть применения паттерна на практике. Проблему может вызвать только необходимость иметь что-то типа фабрики или фабричного метода для создания нужной реализации конкретного поведения объекта. Но это можно легко сделать при помощи механизма внедрения зависимостей (dependency injection), без которого не обходится ни один проект.
Для тех, кто все прочел и так и не понял как работает паттерн стратегия и для чего его применять, предлагаю посмотреть видео, в котором очень понятно и доступно (правда, на английском языке) излагается суть паттерна, особенности его применения и программной реализации. Пример кода в самом конце видео. Приятного просмотра.
Если и после видео не понятно, зачем этот паттерн нужен и кажется, что обычного наследования вполне достаточно и абсолютно не смущает иерархия наследования в десять поколений, то, возможно, паттерны — это не тот краеугольный камень, который сможет помочь писать код лучше. Возможно, стоит вернуться к этому материалу позже.
Спасибо, что дочитали до конца. Надеюсь, информация была полезна.