Всем привет!
Сегодня я хочу вам показать результат взаимодействия двух широкоро распространенных паттернов проектирования Strategy и Composite, в результате чего у нас получится так называемый «Компоновщик стратегий».
Необходимо создать механизм конструирования стратегий для их совместного применения. В жизни может быть использована, например, когда при возникновении нескольких событий выполнять какое-либо действие, события могут быть, например, такие: зарегистрировался пользователь, пользователь заполнил дату рождения, пользователь не заполнил свои паспортные данные — в результате совпадения такой цепочки событий, к примеру, отсылаем нашему ��ользователю email с просьбой заполнить соответствующее поле в анкете. Такие проверки можно осуществлять, к примеру, асинхронно.
Для реализации данной задачи совместим реализации паттернов Strategy и Composite.
Из Wikipedia: Выбор алгоритма, который следует применить, в зависимости от типа выдавшего запрос клиента или обрабатываемых данных.
Из Wikipedia: Паттерн определяет иерархию классов, которые одновременно могут состоять из примитивных и сложных объектов, упрощает архитектуру клиента, делает процесс добавления новых видов объекта более простым.
Для желающих сразу посмотреть реализацию: GitHub source
Сначала определимся, как мы хотим вызывать нашего компоновщика:
В результате чего мы хотим получить стратегию, которая выполнилась успешно. Это может быть или единичный объект или коллекция объектов. (В данном примере, мы видим 2 основные сложные стратегии)
Интерфейс стратегии и компоновщика(т.к. в нашем случае компоновщик сам является стратегией):
Интерфейс компоновщика (самое необходимое):
Напишем базовый компоновщик:
Основной интерес для нас будет составлять метод perform(). В данном случае мы бежим по всем элементам в компоновщике и возвращаем первую стратегию, которая выполнилась.
Далее мы будем расширять реализованный выше базовый класс компоновщика. Реализуем стратегию-компоновщик, которую считаем выполненной, если выполняются все ее стратегии (логическая операция «И»).
Далее по аналогии реализуем стратегию-компоновщик, которую считаем выполненной, если выполняется хотя бы одна ее стратегия (логическая операция «ИЛИ»).
Нам остается реализовать наши стратегии:
Первая и основная:
И еще 4 стратегии:
В реальной жизни, конечно, стратегии будут более сложными.
Не исключаю, что уже существует паттерн для решения подобной задачи.
Комментарии и критика приветствуются :)
Удачных выходных!
Сегодня я хочу вам показать результат взаимодействия двух широкоро распространенных паттернов проектирования Strategy и Composite, в результате чего у нас получится так называемый «Компоновщик стратегий».
Проблема
Необходимо создать механизм конструирования стратегий для их совместного применения. В жизни может быть использована, например, когда при возникновении нескольких событий выполнять какое-либо действие, события могут быть, например, такие: зарегистрировался пользователь, пользователь заполнил дату рождения, пользователь не заполнил свои паспортные данные — в результате совпадения такой цепочки событий, к примеру, отсылаем нашему ��ользователю email с просьбой заполнить соответствующее поле в анкете. Такие проверки можно осуществлять, к примеру, асинхронно.
Для реализации данной задачи совместим реализации паттернов Strategy и Composite.
Задача паттерна Strategy
Из Wikipedia: Выбор алгоритма, который следует применить, в зависимости от типа выдавшего запрос клиента или обрабатываемых данных.
Цель паттерна Composite
Из Wikipedia: Паттерн определяет иерархию классов, которые одновременно могут состоять из примитивных и сложных объектов, упрощает архитектуру клиента, делает процесс добавления новых видов объекта более простым.
Для желающих сразу посмотреть реализацию: GitHub source
Сначала определимся, как мы хотим вызывать нашего компоновщика:
$composite = new \CompositeAndStrategy\CompositeStrategy( new \CompositeAndStrategy\CompositeStrategyAnd( new \CompositeAndStrategy\CompositeStrategyOr( new \CompositeAndStrategy\StrategyFirst(), new \CompositeAndStrategy\StrategySecond() ), new \CompositeAndStrategy\StrategyThird() ), new \CompositeAndStrategy\CompositeStrategyOr( new \CompositeAndStrategy\StrategyFourth(), new \CompositeAndStrategy\StrategyFifth() ) ); $result = $composite->perform();
В результате чего мы хотим получить стратегию, которая выполнилась успешно. Это может быть или единичный объект или коллекция объектов. (В данном примере, мы видим 2 основные сложные стратегии)
Интерфейс стратегии и компоновщика(т.к. в нашем случае компоновщик сам является стратегией):
namespace CompositeAndStrategy; interface IStrategy { function perform(); }
Интерфейс компоновщика (самое необходимое):
namespace CompositeAndStrategy; interface ICompositeStrategy { function getAll(); function add(IStrategy $strategy); }
Напишем базовый компоновщик:
namespace CompositeAndStrategy; class CompositeStrategy implements ICompositeStrategy, IStrategy { public function __construct() { $strategies = func_get_args(); if ($strategies) { foreach($strategies as $strategy) { if ($strategy instanceof IStrategy) { $this->add($strategy); } } } } /** * @var IStrategy[] */ protected $collection; /** * @param IStrategy $strategy */ public function add(IStrategy $strategy) { $this->collection[] = $strategy; } /** * @return IStrategy[] */ public function getAll() { return $this->collection; } /** * @return IStrategy */ public function perform() { foreach($this->getAll() as $strategy) { if ($strategy->perform()) { return $strategy; } } } }
Основной интерес для нас будет составлять метод perform(). В данном случае мы бежим по всем элементам в компоновщике и возвращаем первую стратегию, которая выполнилась.
Далее мы будем расширять реализованный выше базовый класс компоновщика. Реализуем стратегию-компоновщик, которую считаем выполненной, если выполняются все ее стратегии (логическая операция «И»).
namespace CompositeAndStrategy; class CompositeStrategyAnd extends CompositeStrategy { /** * @return bool|CompositeStrategyAnd */ public function perform() { foreach($this->getAll() as $strategy) { if (!$strategy->perform()) { return false; } } return $this; } }
Далее по аналогии реализуем стратегию-компоновщик, которую считаем выполненной, если выполняется хотя бы одна ее стратегия (логическая операция «ИЛИ»).
namespace CompositeAndStrategy; class CompositeStrategyOr extends CompositeStrategy { /** * @return CompositeStrategyOr */ public function perform() { foreach($this->getAll() as $strategy) { if ($strategy->perform()) { return $this; } } } }
Нам остается реализовать наши стратегии:
Первая и основная:
namespace CompositeAndStrategy; class StrategyFirst implements IStrategy { /** * @param $bool * @return mixed */ protected function drawLog($bool) { echo get_called_class().' - ' . (int)$bool.'<hr />'; return $bool; } /** * @return bool|StrategyFirst */ public function perform() { if ($operation = $this->drawLog(rand(0, 1))) { return $this; } return false; } }
И еще 4 стратегии:
namespace CompositeAndStrategy; class StrategySecond extends StrategyFirst { }
namespace CompositeAndStrategy; class StrategyThird extends StrategyFirst { }
namespace CompositeAndStrategy; class StrategyFourth extends StrategyFirst { }
namespace CompositeAndStrategy; class StrategyFifth extends StrategyFirst { }
В реальной жизни, конечно, стратегии будут более сложными.
Не исключаю, что уже существует паттерн для решения подобной задачи.
Комментарии и критика приветствуются :)
Удачных выходных!