Патерны программирования. DesignPatternsphp documentation
Скачать 1.98 Mb.
|
1.3. Поведенческие шаблоны проектирования (Behavioral) 127 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML Код Вы можете найти этот код на GitHub Journey.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\TemplateMethod; 6 7 abstract class Journey 8 { (continues on next page) 128 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 9 /** 10 * @var string[] 11 */ 12 private array $thingsToDo = []; 13 14 /** 15 * This is the public service provided by this class and its subclasses. 16 * Notice it is final to "freeze" the global behavior of algorithm. 17 * If you want to override this contract, make an interface with only takeATrip() 18 * and subclass it. 19 */ 20 final public function takeATrip () 21 { 22 $this -> thingsToDo [] = $this -> buyAFlight (); 23 $this -> thingsToDo [] = $this -> takePlane (); 24 $this -> thingsToDo [] = $this -> enjoyVacation (); 25 $buyGift = $this -> buyGift (); 26 27 if ( $buyGift !== null ) { 28 $this -> thingsToDo [] = $buyGift ; 29 } 30 31 $this -> thingsToDo [] = $this -> takePlane (); 32 } 33 34 /** 35 * This method must be implemented, this is the key-feature of this pattern. 36 */ 37 abstract protected function enjoyVacation () : string; 38 39 /** 40 * This method is also part of the algorithm but it is optional. 41 * You can override it only if you need to 42 */ 43 protected function buyGift () : ? string 44 { 45 return null ; 46 } 47 48 private function buyAFlight () : string 49 { 50 return 'Buy a flight ticket' ; 51 } 52 53 private function takePlane () : string 54 { 55 return 'Taking the plane' ; 56 } 57 58 /** 59 * @return string[] 60 */ (continues on next page) 1.3. Поведенческие шаблоны проектирования (Behavioral) 129 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 61 final public function getThingsToDo () : array 62 { 63 return $this -> thingsToDo ; 64 } 65 } BeachJourney.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\TemplateMethod; 6 7 class BeachJourney extends Journey 8 { 9 protected function enjoyVacation () : string 10 { 11 return "Swimming and sun-bathing" ; 12 } 13 } CityJourney.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\TemplateMethod; 6 7 class CityJourney extends Journey 8 { 9 protected function enjoyVacation () : string 10 { 11 return "Eat, drink, take photos and sleep" ; 12 } 13 14 protected function buyGift () : ? string 15 { 16 return "Buy a gift" ; 17 } 18 } 130 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Тест Tests/JourneyTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\TemplateMethod\Tests; 6 7 use DesignPatterns\Behavioral\TemplateMethod\BeachJourney; 8 use DesignPatterns\Behavioral\TemplateMethod\CityJourney; 9 use PHPUnit\Framework\TestCase; 10 11 class JourneyTest extends TestCase 12 { 13 public function testCanGetOnVacationOnTheBeach () 14 { 15 $beachJourney = new BeachJourney(); 16 $beachJourney -> takeATrip (); 17 18 $this -> assertSame ( 19 [ 'Buy a flight ticket' , 'Taking the plane' , 'Swimming and sun-bathing' , ˓→ 'Taking the plane' ], 20 $beachJourney -> getThingsToDo () 21 ); 22 } 23 24 public function testCanGetOnAJourneyToACity () 25 { 26 $cityJourney = new CityJourney(); 27 $cityJourney -> takeATrip (); 28 29 $this -> assertSame ( 30 [ 31 'Buy a flight ticket' , 32 'Taking the plane' , 33 'Eat, drink, take photos and sleep' , 34 'Buy a gift' , 35 'Taking the plane' 36 ], 37 $cityJourney -> getThingsToDo () 38 ); 39 } 40 } 1.3. Поведенческие шаблоны проектирования (Behavioral) 131 DesignPatternsPHP Documentation, Выпуск 1.0 1.3.13 Посетитель (Visitor) Назначение Шаблон «Посетитель» выполняет операции над объектами других классов. Главной целью является сохранение разделения направленности задач отдельных классов. При этом классы обязаны опреде- лить специальный контракт, чтобы позволить использовать их Посетителям (метод «принять роль» Role::accept в примере). Контракт, как правило, это абстрактный класс, но вы можете использовать чистый интерфейс. В этом случае, каждый посетитель должен сам выбирать, какой метод ссылается на посетителя. 132 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML 1.3. Поведенческие шаблоны проектирования (Behavioral) 133 DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub RoleVisitor.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Visitor; 6 7 /** 8 * Note: the visitor must not choose itself which method to 9 * invoke, it is the visited object that makes this decision 10 */ 11 interface RoleVisitor 12 { 13 public function visitUser (User $role ); 14 15 public function visitGroup (Group $role ); 16 } RecordingVisitor.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Visitor; 6 7 class RecordingVisitor implements RoleVisitor 8 { 9 /** 10 * @var Role[] 11 */ 12 private array $visited = []; 13 14 public function visitGroup (Group $role ) 15 { 16 $this -> visited [] = $role ; 17 } 18 19 public function visitUser (User $role ) 20 { 21 $this -> visited [] = $role ; 22 } 23 24 /** 25 * @return Role[] 26 */ 27 public function getVisited () : array 28 { 29 return $this -> visited ; (continues on next page) 134 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 30 } 31 } Role.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Visitor; 6 7 interface Role 8 { 9 public function accept (RoleVisitor $visitor ); 10 } User.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Visitor; 6 7 class User implements Role 8 { 9 public function __construct ( private string $name ) 10 { 11 } 12 13 public function getName () : string 14 { 15 return sprintf ( 'User %s' , $this -> name ); 16 } 17 18 public function accept (RoleVisitor $visitor ) 19 { 20 $visitor -> visitUser ( $this ); 21 } 22 } Group.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Behavioral\Visitor; 6 7 class Group implements Role 8 { 9 public function __construct ( private string $name ) (continues on next page) 1.3. Поведенческие шаблоны проектирования (Behavioral) 135 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 10 { 11 } 12 13 public function getName () : string 14 { 15 return sprintf ( 'Group: %s' , $this -> name ); 16 } 17 18 public function accept (RoleVisitor $visitor ) 19 { 20 $visitor -> visitGroup ( $this ); 21 } 22 } Тест Tests/VisitorTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Tests\Visitor\Tests; 6 7 use DesignPatterns\Behavioral\Visitor\RecordingVisitor; 8 use DesignPatterns\Behavioral\Visitor\User; 9 use DesignPatterns\Behavioral\Visitor\Group; 10 use DesignPatterns\Behavioral\Visitor\Role; 11 use DesignPatterns\Behavioral\Visitor; 12 use PHPUnit\Framework\TestCase; 13 14 class VisitorTest extends TestCase 15 { 16 private RecordingVisitor $visitor ; 17 18 protected function setUp () : void 19 { 20 $this -> visitor = new RecordingVisitor(); 21 } 22 23 public function provideRoles () 24 { 25 return [ 26 [ new User( 'Dominik' )], 27 [ new Group( 'Administrators' )], 28 ]; 29 } 30 31 /** 32 * @dataProvider provideRoles 33 */ (continues on next page) 136 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 34 public function testVisitSomeRole (Role $role ) 35 { 36 $role -> accept ( $this -> visitor ); 37 $this -> assertSame ( $role , $this -> visitor -> getVisited ()[ 0 ]); 38 } 39 } 1.4 Дополнительно 1.4.1 Локатор Служб (Service Locator) Этот шаблон считается анти-паттерном! Некоторые считают Локатор Служб анти-паттерном. Он нарушает принцип инверсии зависимостей ( Dependency Inversion principle ) из набора принципов SOLID . Локатор Служб скрывает зависимости данного класса вместо их совместного использования, как в случае шаблона Внедрение Зависимости ( Dependency Injection ). В случае изменения данных зависимостей мы рискуем сломать функционал классов, которые их используют, вследствие чего затрудняется поддержка системы. Назначение Для реализации слабосвязанной архитектуры, чтобы получить хорошо тестируемый, сопровождаемый и расширяемый код. Паттерн Инъекция зависимостей (DI) и паттерн Локатор Служб — это реализация паттерна Инверсия управления (Inversion of Control, IoC). Использование С Локатором Служб вы можете зарегистрировать сервис для определенного интерфейса. С помощью интерфейса вы можете получить зарегистрированный сервис и использовать его в классах приложения, не зная его реализацию. Вы можете настроить и внедрить объект Service Locator на начальном этапе сборки приложения. 1.4. Дополнительно 137 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML 138 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub Service.php 1 2 3 namespace DesignPatterns\More\ServiceLocator; 4 5 interface Service 6 { 7 8 } ServiceLocator.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\ServiceLocator; 6 7 use OutOfRangeException; 8 use InvalidArgumentException; 9 10 class ServiceLocator 11 { 12 /** 13 * @var string[][] 14 */ 15 private array $services = []; 16 17 /** 18 * @var Service[] 19 */ 20 private array $instantiated = []; 21 22 public function addInstance (string $class , Service $service ) 23 { 24 $this -> instantiated [ $class ] = $service ; 25 } 26 27 public function addClass (string $class , array $params ) 28 { 29 $this -> services [ $class ] = $params ; 30 } 31 32 public function has (string $interface ) : bool 33 { 34 return isset ( $this -> services [ $interface ]) || isset ( $this -> instantiated [ ˓→ $interface ]); 35 } 36 (continues on next page) 1.4. Дополнительно 139 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 37 public function get (string $class ) : Service 38 { 39 if ( isset ( $this -> instantiated [ $class ])) { 40 return $this -> instantiated [ $class ]; 41 } 42 43 $object = new $class ( $this -> services [ $class ]); 44 45 if ( ! $object instanceof Service) { 46 throw new InvalidArgumentException( 'Could not register service: is no ␣ ˓→ instance of Service' ); 47 } 48 49 $this -> instantiated [ $class ] = $object ; 50 51 return $object ; 52 } 53 } LogService.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\ServiceLocator; 6 7 class LogService implements Service 8 { 9 10 } Тест Tests/ServiceLocatorTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\ServiceLocator\Tests; 6 7 use DesignPatterns\More\ServiceLocator\LogService; 8 use DesignPatterns\More\ServiceLocator\ServiceLocator; 9 use PHPUnit\Framework\TestCase; 10 11 class ServiceLocatorTest extends TestCase 12 { 13 private ServiceLocator $serviceLocator ; 14 15 public function setUp () : void (continues on next page) 140 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 16 { 17 $this -> serviceLocator = new ServiceLocator(); 18 } 19 20 public function testHasServices () 21 { 22 $this -> serviceLocator -> addInstance (LogService :: class , new LogService()); 23 24 $this -> assertTrue ( $this -> serviceLocator -> has (LogService :: class )); 25 $this -> assertFalse ( $this -> serviceLocator -> has (self :: class )); 26 } 27 28 public function testGetWillInstantiateLogServiceIfNoInstanceHasBeenCreatedYet () 29 { 30 $this -> serviceLocator -> addClass (LogService :: class , []); 31 $logger = $this -> serviceLocator -> get (LogService :: class ); 32 33 $this -> assertInstanceOf (LogService :: class , $logger ); 34 } 35 } 1.4.2 Хранилище (Repository) Назначение Посредник между уровнями области определения (хранилище) и распределения данных. Использует интерфейс, похожий на коллекции, для доступа к объектам области определения. Репозиторий ин- капсулирует набор объектов, сохраняемых в хранилище данных, и операции выполняемые над ними, обеспечивая более объектно-ориентированное представление реальных данных. Репозиторий также преследует цель достижения полного разделения и односторонней зависимости между уровнями обла- сти определения и распределения данных. Примеры • Doctrine 2 ORM: в ней есть Repository, который является связующим звеном между Entity и DBAL и содержит методы для получения объектов. • Laravel Framework 1.4. Дополнительно 141 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML Код Вы можете найти этот код на GitHub Post.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\More\Repository\Domain; 6 7 class Post 8 { 9 public static function draft (PostId $id , string $title , string $text ) : Post 10 { (continues on next page) 142 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 11 return new self( 12 $id , 13 PostStatus :: fromString (PostStatus :: STATE_DRAFT ), 14 $title , 15 $text 16 ); 17 } 18 19 public static function fromState ( array $state ) : Post 20 { 21 return new self( 22 PostId :: fromInt ( $state [ 'id' ]), 23 PostStatus :: fromInt ( $state [ 'statusId' ]), 24 $state [ 'title' ], 25 $state [ 'text' ] 26 ); 27 } 28 29 private function __construct ( 30 private PostId $id , 31 private PostStatus $status , 32 private string $title , 33 private string $text 34 ) { 35 } 36 37 public function getId () : PostId 38 { 39 return $this -> id ; 40 } 41 42 public function getStatus () : PostStatus 43 { 44 return $this -> status ; 45 } 46 47 public function getText () : string 48 { 49 return $this -> text ; 50 } 51 52 public function getTitle () : string 53 { 54 return $this -> title ; 55 } 56 } PostId.php |