📜 ⬆️ ⬇️

Strategy Linker or Composite + Strategy Association

Hello!

Today I want to show you the result of the interaction of two wide-spread design patterns Strategy and Composite , with the result that we have the so-called “Strategy Linker”.

Problem


It is necessary to create a mechanism for constructing strategies for their joint use. In life, it can be used, for example, when performing some action when several events occur, the events can be, for example: a user is registered, the user has filled in the date of birth, the user has not filled in his passport data - as a result of the coincidence of such a chain of events, For example, we send our user an email with a request to fill in the appropriate field in the questionnaire. Such checks can be performed, for example, asynchronously.

To accomplish this task, we are compatible with the implementation of the Strategy and Composite patterns.
')

Strategy Pattern Challenge


From Wikipedia: Selection of the algorithm to be applied, depending on the type of client that issued the request or the data being processed.

Purpose of the composite pattern


From Wikipedia: The pattern defines a hierarchy of classes that can simultaneously consist of primitive and complex objects, simplifies the client's architecture, makes the process of adding new types of objects more simple.

For those who want to immediately see the implementation: GitHub source

First, we define how we want to call our linker:
$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(); 


As a result, we want to get a strategy that was executed successfully. This can be either a single object or a collection of objects. (In this example, we see 2 basic complex strategies)

Interface strategy and linker (because in our case the linker itself is a strategy):
 namespace CompositeAndStrategy; interface IStrategy { function perform(); } 


Linker interface (most necessary):
 namespace CompositeAndStrategy; interface ICompositeStrategy { function getAll(); function add(IStrategy $strategy); } 


Write the basic linker:
 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; } } } } 

The main interest for us will be the perform () method. In this case, we run through all the elements in the layout and return the first strategy that has been executed.

Further we will expand the base class of the linker implemented above. We implement a strategy-linker, which we consider to be executed if all its strategies are executed (logical operation “And”).
 namespace CompositeAndStrategy; class CompositeStrategyAnd extends CompositeStrategy { /** * @return bool|CompositeStrategyAnd */ public function perform() { foreach($this->getAll() as $strategy) { if (!$strategy->perform()) { return false; } } return $this; } } 

Further, by analogy, we implement a strategy-linker, which we consider to be executed if at least one of its strategies is executed (logical operation "OR").
 namespace CompositeAndStrategy; class CompositeStrategyOr extends CompositeStrategy { /** * @return CompositeStrategyOr */ public function perform() { foreach($this->getAll() as $strategy) { if ($strategy->perform()) { return $this; } } } } 


It remains for us to implement our strategies:

First and foremost:
 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; } } 


And 4 more strategies:
 namespace CompositeAndStrategy; class StrategySecond extends StrategyFirst { } 

 namespace CompositeAndStrategy; class StrategyThird extends StrategyFirst { } 

 namespace CompositeAndStrategy; class StrategyFourth extends StrategyFirst { } 

 namespace CompositeAndStrategy; class StrategyFifth extends StrategyFirst { } 


In real life, of course, strategies will be more complex.
I do not exclude that there is already a pattern for solving this problem.

Comments and criticism are welcome :)
Have a great weekend!

Source: https://habr.com/ru/post/137190/


All Articles