3.11. Стратегия (Strategy)
3.11.1. Терминология:
Context
Strategy
Concrete Strategy
3.11.2. Назначение
Чтобы разделить стратегии и получить возможность быстрого переключения между ними. Также этот паттерн является хорошей альтернативой наследованию (вместо расширения абстрактного класса).
3.11.3. Примеры
сортировка списка объектов, одна стратегия сортирует по дате, другая по id
упростить юнит тестирование: например переключение между файловым хранилищем и хранилищем в оперативной памяти
3.11.4. Диаграмма UML
3.11.5. Код
Вы можете найти этот код на GitHub
Context.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Strategy;
6
7class Context
8{
9 public function __construct(private Comparator $comparator)
10 {
11 }
12
13 public function executeStrategy(array $elements): array
14 {
15 uasort($elements, [$this->comparator, 'compare']);
16
17 return $elements;
18 }
19}
Comparator.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Strategy;
6
7interface Comparator
8{
9 /**
10 * @param mixed $a
11 * @param mixed $b
12 */
13 public function compare($a, $b): int;
14}
DateComparator.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Strategy;
6
7use DateTime;
8
9class DateComparator implements Comparator
10{
11 public function compare($a, $b): int
12 {
13 $aDate = new DateTime($a['date']);
14 $bDate = new DateTime($b['date']);
15
16 return $aDate <=> $bDate;
17 }
18}
IdComparator.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Strategy;
6
7class IdComparator implements Comparator
8{
9 public function compare($a, $b): int
10 {
11 return $a['id'] <=> $b['id'];
12 }
13}
3.11.6. Тест
Tests/StrategyTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Strategy\Tests;
6
7use DesignPatterns\Behavioral\Strategy\Context;
8use DesignPatterns\Behavioral\Strategy\DateComparator;
9use DesignPatterns\Behavioral\Strategy\IdComparator;
10use PHPUnit\Framework\TestCase;
11
12class StrategyTest extends TestCase
13{
14 public function provideIntegers()
15 {
16 return [
17 [
18 [['id' => 2], ['id' => 1], ['id' => 3]],
19 ['id' => 1],
20 ],
21 [
22 [['id' => 3], ['id' => 2], ['id' => 1]],
23 ['id' => 1],
24 ],
25 ];
26 }
27
28 public function provideDates()
29 {
30 return [
31 [
32 [['date' => '2014-03-03'], ['date' => '2015-03-02'], ['date' => '2013-03-01']],
33 ['date' => '2013-03-01'],
34 ],
35 [
36 [['date' => '2014-02-03'], ['date' => '2013-02-01'], ['date' => '2015-02-02']],
37 ['date' => '2013-02-01'],
38 ],
39 ];
40 }
41
42 /**
43 * @dataProvider provideIntegers
44 *
45 * @param array $collection
46 * @param array $expected
47 */
48 public function testIdComparator($collection, $expected)
49 {
50 $obj = new Context(new IdComparator());
51 $elements = $obj->executeStrategy($collection);
52
53 $firstElement = array_shift($elements);
54 $this->assertSame($expected, $firstElement);
55 }
56
57 /**
58 * @dataProvider provideDates
59 *
60 * @param array $collection
61 * @param array $expected
62 */
63 public function testDateComparator($collection, $expected)
64 {
65 $obj = new Context(new DateComparator());
66 $elements = $obj->executeStrategy($collection);
67
68 $firstElement = array_shift($elements);
69 $this->assertSame($expected, $firstElement);
70 }
71}