3.10. Strategy
3.10.1. Terminology:
- Context
- Strategy
- Concrete Strategy
3.10.2. Purpose
To separate strategies and to enable fast switching between them. Alsothis pattern is a good alternative to inheritance (instead of having anabstract class that is extended).
3.10.3. Examples
- sorting a list of objects, one strategy by date, the other by id
- simplify unit testing: e.g. switching between file and in-memorystorage
3.10.4. UML Diagram
3.10.5. Code
You can also find this code on GitHub
Context.php
- <?php
- namespace DesignPatterns\Behavioral\Strategy;
- class Context
- {
- /**
- * @var ComparatorInterface
- */
- private $comparator;
- public function __construct(ComparatorInterface $comparator)
- {
- $this->comparator = $comparator;
- }
- public function executeStrategy(array $elements) : array
- {
- uasort($elements, [$this->comparator, 'compare']);
- return $elements;
- }
- }
ComparatorInterface.php
- <?php
- namespace DesignPatterns\Behavioral\Strategy;
- interface ComparatorInterface
- {
- /**
- * @param mixed $a
- * @param mixed $b
- *
- * @return int
- */
- public function compare($a, $b): int;
- }
DateComparator.php
- <?php
- namespace DesignPatterns\Behavioral\Strategy;
- class DateComparator implements ComparatorInterface
- {
- /**
- * @param mixed $a
- * @param mixed $b
- *
- * @return int
- */
- public function compare($a, $b): int
- {
- $aDate = new \DateTime($a['date']);
- $bDate = new \DateTime($b['date']);
- return $aDate <=> $bDate;
- }
- }
IdComparator.php
- <?php
- namespace DesignPatterns\Behavioral\Strategy;
- class IdComparator implements ComparatorInterface
- {
- /**
- * @param mixed $a
- * @param mixed $b
- *
- * @return int
- */
- public function compare($a, $b): int
- {
- return $a['id'] <=> $b['id'];
- }
- }
3.10.6. Test
Tests/StrategyTest.php
- <?php
- namespace DesignPatterns\Behavioral\Strategy\Tests;
- use DesignPatterns\Behavioral\Strategy\Context;
- use DesignPatterns\Behavioral\Strategy\DateComparator;
- use DesignPatterns\Behavioral\Strategy\IdComparator;
- use PHPUnit\Framework\TestCase;
- class StrategyTest extends TestCase
- {
- public function provideIntegers()
- {
- return [
- [
- [['id' => 2], ['id' => 1], ['id' => 3]],
- ['id' => 1],
- ],
- [
- [['id' => 3], ['id' => 2], ['id' => 1]],
- ['id' => 1],
- ],
- ];
- }
- public function provideDates()
- {
- return [
- [
- [['date' => '2014-03-03'], ['date' => '2015-03-02'], ['date' => '2013-03-01']],
- ['date' => '2013-03-01'],
- ],
- [
- [['date' => '2014-02-03'], ['date' => '2013-02-01'], ['date' => '2015-02-02']],
- ['date' => '2013-02-01'],
- ],
- ];
- }
- /**
- * @dataProvider provideIntegers
- *
- * @param array $collection
- * @param array $expected
- */
- public function testIdComparator($collection, $expected)
- {
- $obj = new Context(new IdComparator());
- $elements = $obj->executeStrategy($collection);
- $firstElement = array_shift($elements);
- $this->assertEquals($expected, $firstElement);
- }
- /**
- * @dataProvider provideDates
- *
- * @param array $collection
- * @param array $expected
- */
- public function testDateComparator($collection, $expected)
- {
- $obj = new Context(new DateComparator());
- $elements = $obj->executeStrategy($collection);
- $firstElement = array_shift($elements);
- $this->assertEquals($expected, $firstElement);
- }
- }