Всем привет!
Сегодня я хочу вам показать результат взаимодействия двух широкоро распространенных паттернов проектирования 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
{
}
В реальной жизни, конечно, стратегии будут более сложными.
Не исключаю, что уже существует паттерн для решения подобной задачи.
Комментарии и критика приветствуются :)
Удачных выходных!