Патерны программирования. DesignPatternsphp documentation
Скачать 1.98 Mb.
|
= 1 ); 4 5 namespace DesignPatterns\Behavioral\Specification; 6 7 interface Specification 8 { 9 public function isSatisfiedBy (Item $item ) : bool; 10 } OrSpecification.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Specification; 6 7 class OrSpecification implements Specification 8 { 9 /** 10 * @var Specification[] 11 */ 12 private array $specifications ; 13 14 /** 15 * @param Specification[] $specifications (continues on next page) 114 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 16 */ 17 public function __construct (Specification $specifications ) 18 { 19 $this -> specifications = $specifications ; 20 } 21 22 /* 23 * if at least one specification is true, return true, else return false 24 */ 25 public function isSatisfiedBy (Item $item ) : bool 26 { 27 foreach ( $this -> specifications as $specification ) { 28 if ( $specification -> isSatisfiedBy ( $item )) { 29 return true ; 30 } 31 } 32 33 return false ; 34 } 35 } PriceSpecification.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Specification; 6 7 class PriceSpecification implements Specification 8 { 9 public function __construct ( private ? float $minPrice , private ? float $maxPrice ) 10 { 11 } 12 13 public function isSatisfiedBy (Item $item ) : bool 14 { 15 if ( $this -> maxPrice !== null && $item -> getPrice () > $this -> maxPrice ) { 16 return false ; 17 } 18 19 if ( $this -> minPrice !== null && $item -> getPrice () < $this -> minPrice ) { 20 return false ; 21 } 22 23 return true ; 24 } 25 } AndSpecification.php 1 (continues on next page) 1.3. Поведенческие шаблоны проектирования (Behavioral) 115 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Specification; 6 7 class AndSpecification implements Specification 8 { 9 /** 10 * @var Specification[] 11 */ 12 private array $specifications ; 13 14 /** 15 * @param Specification[] $specifications 16 */ 17 public function __construct (Specification $specifications ) 18 { 19 $this -> specifications = $specifications ; 20 } 21 22 /** 23 * if at least one specification is false, return false, else return true. 24 */ 25 public function isSatisfiedBy (Item $item ) : bool 26 { 27 foreach ( $this -> specifications as $specification ) { 28 if ( ! $specification -> isSatisfiedBy ( $item )) { 29 return false ; 30 } 31 } 32 33 return true ; 34 } 35 } NotSpecification.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Specification; 6 7 class NotSpecification implements Specification 8 { 9 public function __construct ( private Specification $specification ) 10 { 11 } 12 13 public function isSatisfiedBy (Item $item ) : bool 14 { 15 return ! $this -> specification -> isSatisfiedBy ( $item ); (continues on next page) 116 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 16 } 17 } Тест Tests/SpecificationTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Specification\Tests; 6 7 use DesignPatterns\Behavioral\Specification\Item; 8 use DesignPatterns\Behavioral\Specification\NotSpecification; 9 use DesignPatterns\Behavioral\Specification\OrSpecification; 10 use DesignPatterns\Behavioral\Specification\AndSpecification; 11 use DesignPatterns\Behavioral\Specification\PriceSpecification; 12 use PHPUnit\Framework\TestCase; 13 14 class SpecificationTest extends TestCase 15 { 16 public function testCanOr () 17 { 18 $spec1 = new PriceSpecification( 50 , 99 ); 19 $spec2 = new PriceSpecification( 101 , 200 ); 20 21 $orSpec = new OrSpecification( $spec1 , $spec2 ); 22 23 $this -> assertFalse ( $orSpec -> isSatisfiedBy ( new Item( 100 ))); 24 $this -> assertTrue ( $orSpec -> isSatisfiedBy ( new Item( 51 ))); 25 $this -> assertTrue ( $orSpec -> isSatisfiedBy ( new Item( 150 ))); 26 } 27 28 public function testCanAnd () 29 { 30 $spec1 = new PriceSpecification( 50 , 100 ); 31 $spec2 = new PriceSpecification( 80 , 200 ); 32 33 $andSpec = new AndSpecification( $spec1 , $spec2 ); 34 35 $this -> assertFalse ( $andSpec -> isSatisfiedBy ( new Item( 150 ))); 36 $this -> assertFalse ( $andSpec -> isSatisfiedBy ( new Item( 1 ))); 37 $this -> assertFalse ( $andSpec -> isSatisfiedBy ( new Item( 51 ))); 38 $this -> assertTrue ( $andSpec -> isSatisfiedBy ( new Item( 100 ))); 39 } 40 41 public function testCanNot () 42 { 43 $spec1 = new PriceSpecification( 50 , 100 ); 44 $notSpec = new NotSpecification( $spec1 ); (continues on next page) 1.3. Поведенческие шаблоны проектирования (Behavioral) 117 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 45 46 $this -> assertTrue ( $notSpec -> isSatisfiedBy ( new Item( 150 ))); 47 $this -> assertFalse ( $notSpec -> isSatisfiedBy ( new Item( 50 ))); 48 } 49 } 1.3.10 Состояние (State) Назначение Инкапсулирует изменение поведения одних и тех же методов в зависимости от состояния объекта. Этот паттерн поможет изящным способом изменить поведение объекта во время выполнения не прибегая к большим монолитным условным операторам. Диаграмма UML 118 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub OrderContext.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\State; 6 7 class OrderContext 8 { 9 private State $state ; 10 11 public static function create () : OrderContext 12 { 13 $order = new self(); 14 $order -> state = new StateCreated(); 15 16 return $order ; 17 } 18 19 public function setState (State $state ) 20 { 21 $this -> state = $state ; 22 } 23 24 public function proceedToNext () 25 { 26 $this -> state -> proceedToNext ( $this ); 27 } 28 29 public function toString () 30 { 31 return $this -> state -> toString (); 32 } 33 } State.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\State; 6 7 interface State 8 { 9 public function proceedToNext (OrderContext $context ); 10 11 public function toString () : string; 12 } 1.3. Поведенческие шаблоны проектирования (Behavioral) 119 DesignPatternsPHP Documentation, Выпуск 1.0 StateCreated.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\State; 6 7 class StateCreated implements State 8 { 9 public function proceedToNext (OrderContext $context ) 10 { 11 $context -> setState ( new StateShipped()); 12 } 13 14 public function toString () : string 15 { 16 return 'created' ; 17 } 18 } StateShipped.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\State; 6 7 class StateShipped implements State 8 { 9 public function proceedToNext (OrderContext $context ) 10 { 11 $context -> setState ( new StateDone()); 12 } 13 14 public function toString () : string 15 { 16 return 'shipped' ; 17 } 18 } StateDone.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\State; 6 7 class StateDone implements State 8 { 9 public function proceedToNext (OrderContext $context ) 10 { (continues on next page) 120 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 11 // there is nothing more to do 12 } 13 14 public function toString () : string 15 { 16 return 'done' ; 17 } 18 } Тест Tests/StateTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\State\Tests; 6 7 use DesignPatterns\Behavioral\State\OrderContext; 8 use PHPUnit\Framework\TestCase; 9 10 class StateTest extends TestCase 11 { 12 public function testIsCreatedWithStateCreated () 13 { 14 $orderContext = OrderContext :: create (); 15 16 $this -> assertSame ( 'created' , $orderContext -> toString ()); 17 } 18 19 public function testCanProceedToStateShipped () 20 { 21 $contextOrder = OrderContext :: create (); 22 $contextOrder -> proceedToNext (); 23 24 $this -> assertSame ( 'shipped' , $contextOrder -> toString ()); 25 } 26 27 public function testCanProceedToStateDone () 28 { 29 $contextOrder = OrderContext :: create (); 30 $contextOrder -> proceedToNext (); 31 $contextOrder -> proceedToNext (); 32 33 $this -> assertSame ( 'done' , $contextOrder -> toString ()); 34 } 35 36 public function testStateDoneIsTheLastPossibleState () 37 { 38 $contextOrder = OrderContext :: create (); (continues on next page) 1.3. Поведенческие шаблоны проектирования (Behavioral) 121 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 39 $contextOrder -> proceedToNext (); 40 $contextOrder -> proceedToNext (); 41 $contextOrder -> proceedToNext (); 42 43 $this -> assertSame ( 'done' , $contextOrder -> toString ()); 44 } 45 } 1.3.11 Стратегия (Strategy) Терминология: • Context • Strategy • Concrete Strategy Назначение Чтобы разделить стратегии и получить возможность быстрого переключения между ними. Также этот паттерн является хорошей альтернативой наследованию (вместо расширения абстрактного класса). Примеры • сортировка списка объектов, одна стратегия сортирует по дате, другая по id • упростить юнит тестирование: например переключение между файловым хранилищем и храни- лищем в оперативной памяти 122 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML Код Вы можете найти этот код на GitHub Context.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Strategy; (continues on next page) 1.3. Поведенческие шаблоны проектирования (Behavioral) 123 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 6 7 class 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 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Strategy; 6 7 interface Comparator 8 { 9 /** 10 * @param mixed $a 11 * @param mixed $b 12 */ 13 public function compare ( $a , $b ) : int; 14 } DateComparator.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Strategy; 6 7 use DateTime; 8 9 class 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 } 124 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 IdComparator.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Strategy; 6 7 class IdComparator implements Comparator 8 { 9 public function compare ( $a , $b ) : int 10 { 11 return $a [ 'id' ] <=> $b [ 'id' ]; 12 } 13 } Тест Tests/StrategyTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Strategy\Tests; 6 7 use DesignPatterns\Behavioral\Strategy\Context; 8 use DesignPatterns\Behavioral\Strategy\DateComparator; 9 use DesignPatterns\Behavioral\Strategy\IdComparator; 10 use PHPUnit\Framework\TestCase; 11 12 class 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- (continues on next page) 1.3. Поведенческие шаблоны проектирования (Behavioral) 125 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) ˓→ 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 } 126 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 1.3.12 Шаблонный Метод (Template Method) Назначение Шаблонный метод, это поведенческий паттерн проектирования. Возможно, вы сталкивались с этим уже много раз. Идея состоит в том, чтобы позволить наследникам абстрактного шаблона переопределить поведение алгоритмов родителя. Как в «Голливудском принципе»: «Не звоните нам, мы сами вам позвоним». Этот класс не вызывается подклассами, но наоборот: подклассы вызываются родителем. Как? С помощью метода в родительской абстракции, конечно. Другими словами, это каркас алгоритма, который хорошо подходит для библиотек (в фреймворках, например). Пользователь просто реализует уточняющие методы, а суперкласс делает всю основную работу. Это простой способ изолировать логику в конкретные классы и уменьшить копипаст, поэтому вы повсеместно встретите его в том или ином виде. |