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

Alt Strategy UML Diagram

3.10.5. Code

You can also find this code on GitHub

Context.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Strategy;
  4.  
  5. class Context
  6. {
  7. /**
  8. * @var ComparatorInterface
  9. */
  10. private $comparator;
  11.  
  12. public function __construct(ComparatorInterface $comparator)
  13. {
  14. $this->comparator = $comparator;
  15. }
  16.  
  17. public function executeStrategy(array $elements) : array
  18. {
  19. uasort($elements, [$this->comparator, 'compare']);
  20.  
  21. return $elements;
  22. }
  23. }

ComparatorInterface.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Strategy;
  4.  
  5. interface ComparatorInterface
  6. {
  7. /**
  8. * @param mixed $a
  9. * @param mixed $b
  10. *
  11. * @return int
  12. */
  13. public function compare($a, $b): int;
  14. }

DateComparator.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Strategy;
  4.  
  5. class DateComparator implements ComparatorInterface
  6. {
  7. /**
  8. * @param mixed $a
  9. * @param mixed $b
  10. *
  11. * @return int
  12. */
  13. public function compare($a, $b): int
  14. {
  15. $aDate = new \DateTime($a['date']);
  16. $bDate = new \DateTime($b['date']);
  17.  
  18. return $aDate <=> $bDate;
  19. }
  20. }

IdComparator.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Strategy;
  4.  
  5. class IdComparator implements ComparatorInterface
  6. {
  7. /**
  8. * @param mixed $a
  9. * @param mixed $b
  10. *
  11. * @return int
  12. */
  13. public function compare($a, $b): int
  14. {
  15. return $a['id'] <=> $b['id'];
  16. }
  17. }

3.10.6. Test

Tests/StrategyTest.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Strategy\Tests;
  4.  
  5. use DesignPatterns\Behavioral\Strategy\Context;
  6. use DesignPatterns\Behavioral\Strategy\DateComparator;
  7. use DesignPatterns\Behavioral\Strategy\IdComparator;
  8. use PHPUnit\Framework\TestCase;
  9.  
  10. class StrategyTest extends TestCase
  11. {
  12. public function provideIntegers()
  13. {
  14. return [
  15. [
  16. [['id' => 2], ['id' => 1], ['id' => 3]],
  17. ['id' => 1],
  18. ],
  19. [
  20. [['id' => 3], ['id' => 2], ['id' => 1]],
  21. ['id' => 1],
  22. ],
  23. ];
  24. }
  25.  
  26. public function provideDates()
  27. {
  28. return [
  29. [
  30. [['date' => '2014-03-03'], ['date' => '2015-03-02'], ['date' => '2013-03-01']],
  31. ['date' => '2013-03-01'],
  32. ],
  33. [
  34. [['date' => '2014-02-03'], ['date' => '2013-02-01'], ['date' => '2015-02-02']],
  35. ['date' => '2013-02-01'],
  36. ],
  37. ];
  38. }
  39.  
  40. /**
  41. * @dataProvider provideIntegers
  42. *
  43. * @param array $collection
  44. * @param array $expected
  45. */
  46. public function testIdComparator($collection, $expected)
  47. {
  48. $obj = new Context(new IdComparator());
  49. $elements = $obj->executeStrategy($collection);
  50.  
  51. $firstElement = array_shift($elements);
  52. $this->assertEquals($expected, $firstElement);
  53. }
  54.  
  55. /**
  56. * @dataProvider provideDates
  57. *
  58. * @param array $collection
  59. * @param array $expected
  60. */
  61. public function testDateComparator($collection, $expected)
  62. {
  63. $obj = new Context(new DateComparator());
  64. $elements = $obj->executeStrategy($collection);
  65.  
  66. $firstElement = array_shift($elements);
  67. $this->assertEquals($expected, $firstElement);
  68. }
  69. }