Патерны программирования. DesignPatternsphp documentation
Скачать 1.98 Mb.
|
DesignPatternsPHP Documentation Выпуск 1.0 Dominik Liebler and contributors нояб. 14, 2022 Оглавление 1 Паттерны 3 1.1 Порождающие шаблоны проектирования (Creational) 3 1.1.1 Абстрактная фабрика (Abstract Factory) 3 1.1.2 Строитель (Builder) 9 1.1.3 Фабричный Метод (Factory Method) . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.1.4 Объектный пул (Pool) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.1.5 Прототип (Prototype) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 1.1.6 Простая Фабрика (Simple Factory) . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 1.1.7 Одиночка (Singleton) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 1.1.8 Статическая Фабрика (Static Factory) . . . . . . . . . . . . . . . . . . . . . . . . . 30 1.2 Структурные шаблоны проектирования (Structural) . . . . . . . . . . . . . . . . . . . . . 33 1.2.1 Адаптер (Adapter / Wrapper) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1.2.2 Мост (Bridge) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 1.2.3 Компоновщик (Composite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 1.2.4 Преобразователь Данных (Data Mapper) . . . . . . . . . . . . . . . . . . . . . . . . 46 1.2.5 Декоратор (Decorator) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 1.2.6 Внедрение Зависимости (Dependency Injection) . . . . . . . . . . . . . . . . . . . . 54 1.2.7 Фасад (Facade) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 1.2.8 Текучий Интерфейс (Fluent Interface) . . . . . . . . . . . . . . . . . . . . . . . . . 61 1.2.9 Приспособленец (Flyweight) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 1.2.10 Прокси (Proxy) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 1.2.11 Реестр (Registry) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 1.3 Поведенческие шаблоны проектирования (Behavioral) . . . . . . . . . . . . . . . . . . . . 76 1.3.1 Цепочка Обязанностей (Chain Of Responsibilities) . . . . . . . . . . . . . . . . . . 76 1.3.2 Команда (Command) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 1.3.3 Интепретатор (Interpreter) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 1.3.4 Итератор (Iterator) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 1.3.5 Посредник (Mediator) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 1.3.6 Хранитель (Memento) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 1.3.7 Объект Null (Null Object) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 1.3.8 Наблюдатель (Observer) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 1.3.9 Спецификация (Specification) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 1.3.10 Состояние (State) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 1.3.11 Стратегия (Strategy) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 1.3.12 Шаблонный Метод (Template Method) . . . . . . . . . . . . . . . . . . . . . . . . . 127 1.3.13 Посетитель (Visitor) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 i 1.4 Дополнительно . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 1.4.1 Локатор Служб (Service Locator) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 1.4.2 Хранилище (Repository) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 1.4.3 Сущность-Атрибут-Значение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 ii DesignPatternsPHP Documentation, Выпуск 1.0 This is a collection of known design patterns and some sample code how to implement them in PHP. Every pattern has a small list of examples. Я считаю, проблема паттернов в том, что люди часто знакомы с ними, но не представляют как их применять. Оглавление 1 DesignPatternsPHP Documentation, Выпуск 1.0 2 Оглавление Глава 1 Паттерны Паттерны могут быть условно сгруппированы в три различные категории. Нажмите на заголовок каждой страницы с паттерном для детального объяснения паттерна в Википедии. 1.1 Порождающие шаблоны проектирования (Creational) В разработке программного обеспечения, Порождающие шаблоны проектирования – это паттерны, которые имеют дело с механизмами создания объекта и пытаются создать объекты в порядке, подхо- дящем к ситуации. Обычная форма создания объекта может привести к проблемам проектирования или увеличивать сложность конструкции. Порождающие шаблоны проектирования решают эту про- блему, определённым образом контролируя процесс создания объекта. 1.1.1 Абстрактная фабрика (Abstract Factory) Назначение To create series of related or dependent objects without specifying their concrete classes. Usually the created classes all implement the same interface. The client of the abstract factory does not care about how these objects are created, it just knows how they go together. 3 DesignPatternsPHP Documentation, Выпуск 1.0 4 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML 1.1. Порождающие шаблоны проектирования (Creational) 5 DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub WriterFactory.php 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 interface WriterFactory 6 { 7 public function createCsvWriter () : CsvWriter; 8 public function createJsonWriter () : JsonWriter; 9 } CsvWriter.php 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 interface CsvWriter 6 { 7 public function write ( array $line ) : string; 8 } JsonWriter.php 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 interface JsonWriter 6 { 7 public function write ( array $data , bool $formatted ) : string; 8 } UnixCsvWriter.php 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 class UnixCsvWriter implements CsvWriter 6 { 7 public function write ( array $line ) : string 8 { 9 return join ( ',' , $line ) "\n" ; 10 } 11 } UnixJsonWriter.php 6 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 class UnixJsonWriter implements JsonWriter 6 { 7 public function write ( array $data , bool $formatted ) : string 8 { 9 $options = 0 ; 10 11 if ( $formatted ) { 12 $options = JSON_PRETTY_PRINT; 13 } 14 15 return json_encode ( $data , $options ); 16 } 17 } UnixWriterFactory.php 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 class UnixWriterFactory implements WriterFactory 6 { 7 public function createCsvWriter () : CsvWriter 8 { 9 return new UnixCsvWriter(); 10 } 11 12 public function createJsonWriter () : JsonWriter 13 { 14 return new UnixJsonWriter(); 15 } 16 } WinCsvWriter.php 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 class WinCsvWriter implements CsvWriter 6 { 7 public function write ( array $line ) : string 8 { 9 return join ( ',' , $line ) "\r\n" ; 10 } 11 } WinJsonWriter.php 1.1. Порождающие шаблоны проектирования (Creational) 7 DesignPatternsPHP Documentation, Выпуск 1.0 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 class WinJsonWriter implements JsonWriter 6 { 7 public function write ( array $data , bool $formatted ) : string 8 { 9 $options = 0 ; 10 11 if ( $formatted ) { 12 $options = JSON_PRETTY_PRINT; 13 } 14 15 return json_encode ( $data , $options ); 16 } 17 } WinWriterFactory.php 1 2 3 namespace DesignPatterns\Creational\AbstractFactory; 4 5 class WinWriterFactory implements WriterFactory 6 { 7 public function createCsvWriter () : CsvWriter 8 { 9 return new WinCsvWriter(); 10 } 11 12 public function createJsonWriter () : JsonWriter 13 { 14 return new WinJsonWriter(); 15 } 16 } Тест Tests/AbstractFactoryTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\AbstractFactory\Tests; 6 7 use DesignPatterns\Creational\AbstractFactory\CsvWriter; 8 use DesignPatterns\Creational\AbstractFactory\JsonWriter; 9 use DesignPatterns\Creational\AbstractFactory\UnixWriterFactory; 10 use DesignPatterns\Creational\AbstractFactory\WinWriterFactory; 11 use DesignPatterns\Creational\AbstractFactory\WriterFactory; (continues on next page) 8 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 12 use PHPUnit\Framework\TestCase; 13 14 class AbstractFactoryTest extends TestCase 15 { 16 public function provideFactory () 17 { 18 return [ 19 [ new UnixWriterFactory()], 20 [ new WinWriterFactory()] 21 ]; 22 } 23 24 /** 25 * @dataProvider provideFactory 26 */ 27 public function testCanCreateCsvWriterOnUnix (WriterFactory $writerFactory ) 28 { 29 $this -> assertInstanceOf (JsonWriter :: class , $writerFactory -> createJsonWriter ()); 30 $this -> assertInstanceOf (CsvWriter :: class , $writerFactory -> createCsvWriter ()); 31 } 32 } 1.1.2 Строитель (Builder) Назначение Строитель — это интерфейс для производства частей сложного объекта. Иногда, если Строитель лучше знает о том, что он строит, этот интерфейс может быть абстрактным классом с методами по-умолчанию (адаптер). Если у вас есть сложное дерево наследования для объектов, логично иметь сложное дерево наследо- вания и для их строителей. Примечание: Строители могут иметь текучий интерфейс , например, строитель макетов в PHPUnit. Примеры • PHPUnit: Mock Builder 1.1. Порождающие шаблоны проектирования (Creational) 9 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML Код Вы можете найти этот код на GitHub Director.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder; 6 7 use DesignPatterns\Creational\Builder\Parts\Vehicle; 8 9 /** 10 * Director is part of the builder pattern. It knows the interface of the builder 11 * and builds a complex object with the help of the builder 12 * 13 * You can also inject many builders instead of one to build more complex objects 14 */ (continues on next page) 10 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 15 class Director 16 { 17 public function build (Builder $builder ) : Vehicle 18 { 19 $builder -> createVehicle (); 20 $builder -> addDoors (); 21 $builder -> addEngine (); 22 $builder -> addWheel (); 23 24 return $builder -> getVehicle (); 25 } 26 } Builder.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder; 6 7 use DesignPatterns\Creational\Builder\Parts\Vehicle; 8 9 interface Builder 10 { 11 public function createVehicle () : void; 12 13 public function addWheel () : void; 14 15 public function addEngine () : void; 16 17 public function addDoors () : void; 18 19 public function getVehicle () : Vehicle; 20 } TruckBuilder.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder; 6 7 use DesignPatterns\Creational\Builder\Parts\Door; 8 use DesignPatterns\Creational\Builder\Parts\Engine; 9 use DesignPatterns\Creational\Builder\Parts\Wheel; 10 use DesignPatterns\Creational\Builder\Parts\Truck; 11 use DesignPatterns\Creational\Builder\Parts\Vehicle; 12 13 class TruckBuilder implements Builder 14 { (continues on next page) 1.1. Порождающие шаблоны проектирования (Creational) 11 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 15 private Truck $truck ; 16 17 public function addDoors () : void 18 { 19 $this -> truck -> setPart ( 'rightDoor' , new Door()); 20 $this -> truck -> setPart ( 'leftDoor' , new Door()); 21 } 22 23 public function addEngine () : void 24 { 25 $this -> truck -> setPart ( 'truckEngine' , new Engine()); 26 } 27 28 public function addWheel () : void 29 { 30 $this -> truck -> setPart ( 'wheel1' , new Wheel()); 31 $this -> truck -> setPart ( 'wheel2' , new Wheel()); 32 $this -> truck -> setPart ( 'wheel3' , new Wheel()); 33 $this -> truck -> setPart ( 'wheel4' , new Wheel()); 34 $this -> truck -> setPart ( 'wheel5' , new Wheel()); 35 $this -> truck -> setPart ( 'wheel6' , new Wheel()); 36 } 37 38 public function createVehicle () : void 39 { 40 $this -> truck = new Truck(); 41 } 42 43 public function getVehicle () : Vehicle 44 { 45 return $this -> truck ; 46 } 47 } CarBuilder.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder; 6 7 use DesignPatterns\Creational\Builder\Parts\Door; 8 use DesignPatterns\Creational\Builder\Parts\Engine; 9 use DesignPatterns\Creational\Builder\Parts\Wheel; 10 use DesignPatterns\Creational\Builder\Parts\Car; 11 use DesignPatterns\Creational\Builder\Parts\Vehicle; 12 13 class CarBuilder implements Builder 14 { 15 private Car $car ; 16 (continues on next page) 12 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 17 public function addDoors () : void 18 { 19 $this -> car -> setPart ( 'rightDoor' , new Door()); 20 $this -> car -> setPart ( 'leftDoor' , new Door()); 21 $this -> car -> setPart ( 'trunkLid' , new Door()); 22 } 23 24 public function addEngine () : void 25 { 26 $this -> car -> setPart ( 'engine' , new Engine()); 27 } 28 29 public function addWheel () : void 30 { 31 $this -> car -> setPart ( 'wheelLF' , new Wheel()); 32 $this -> car -> setPart ( 'wheelRF' , new Wheel()); 33 $this -> car -> setPart ( 'wheelLR' , new Wheel()); 34 $this -> car -> setPart ( 'wheelRR' , new Wheel()); 35 } 36 37 public function createVehicle () : void 38 { 39 $this -> car = new Car(); 40 } 41 42 public function getVehicle () : Vehicle 43 { 44 return $this -> car ; 45 } 46 } Parts/Vehicle.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder\Parts; 6 7 abstract class Vehicle 8 { 9 final public function setPart (string $key , object $value ) 10 { 11 } 12 } Parts/Truck.php 1 2 3 declare (strict_types = 1 ); 4 (continues on next page) 1.1. Порождающие шаблоны проектирования (Creational) 13 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 5 namespace DesignPatterns\Creational\Builder\Parts; 6 7 class Truck extends Vehicle 8 { 9 } Parts/Car.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder\Parts; 6 7 class Car extends Vehicle 8 { 9 } Parts/Engine.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder\Parts; 6 7 class Engine 8 { 9 } Parts/Wheel.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder\Parts; 6 7 class Wheel 8 { 9 } Parts/Door.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder\Parts; 6 7 class Door 8 { 9 } 14 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Тест Tests/DirectorTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Builder\Tests; 6 7 use DesignPatterns\Creational\Builder\Parts\Car; 8 use DesignPatterns\Creational\Builder\Parts\Truck; 9 use DesignPatterns\Creational\Builder\TruckBuilder; 10 use DesignPatterns\Creational\Builder\CarBuilder; 11 use DesignPatterns\Creational\Builder\Director; 12 use PHPUnit\Framework\TestCase; 13 14 class DirectorTest extends TestCase 15 { 16 public function testCanBuildTruck () 17 { 18 $truckBuilder = new TruckBuilder(); 19 $newVehicle = ( new Director()) -> build ( $truckBuilder ); 20 21 $this -> assertInstanceOf (Truck :: class , $newVehicle ); 22 } 23 24 public function testCanBuildCar () 25 { 26 $carBuilder = new CarBuilder(); 27 $newVehicle = ( new Director()) -> build ( $carBuilder ); 28 29 $this -> assertInstanceOf (Car :: class , $newVehicle ); 30 } 31 } 1.1.3 Фабричный Метод (Factory Method) Назначение Выгодное отличие от SimpleFactory в том, что вы можете вынести реализацию создания объектов в подклассы. В простых случаях, этот абстрактный класс может быть только интерфейсом. Этот паттерн является «настоящим» Шаблоном Проектирования, потому что он следует «Принципу инверсии зависимостей» также известному как «D» в S.O.L.I.D Это означает, что класс FactoryMethod зависит от абстракций, а не от конкретных классов. Это суще- ственный плюс в сравнении с SimpleFactory или StaticFactory. 1.1. Порождающие шаблоны проектирования (Creational) 15 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML 16 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub Logger.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\FactoryMethod; 6 7 interface Logger 8 { 9 public function log (string $message ); 10 } StdoutLogger.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\FactoryMethod; 6 7 class StdoutLogger implements Logger 8 { 9 public function log (string $message ) 10 { 11 echo $message ; 12 } 13 } FileLogger.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\FactoryMethod; 6 7 class FileLogger implements Logger 8 { 9 public function __construct ( private string $filePath ) 10 { 11 } 12 13 public function log (string $message ) 14 { 15 file_put_contents ( $this -> filePath , $message PHP_EOL, FILE_APPEND); 16 } 17 } LoggerFactory.php 1.1. Порождающие шаблоны проектирования (Creational) 17 DesignPatternsPHP Documentation, Выпуск 1.0 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\FactoryMethod; 6 7 interface LoggerFactory 8 { 9 public function createLogger () : Logger; 10 } StdoutLoggerFactory.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\FactoryMethod; 6 7 class StdoutLoggerFactory implements LoggerFactory 8 { 9 public function createLogger () : Logger 10 { 11 return new StdoutLogger(); 12 } 13 } FileLoggerFactory.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\FactoryMethod; 6 7 class FileLoggerFactory implements LoggerFactory 8 { 9 public function __construct ( private string $filePath ) 10 { 11 } 12 13 public function createLogger () : Logger 14 { 15 return new FileLogger( $this -> filePath ); 16 } 17 } 18 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Тест Tests/FactoryMethodTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\FactoryMethod\Tests; 6 7 use DesignPatterns\Creational\FactoryMethod\FileLogger; 8 use DesignPatterns\Creational\FactoryMethod\FileLoggerFactory; 9 use DesignPatterns\Creational\FactoryMethod\StdoutLogger; 10 use DesignPatterns\Creational\FactoryMethod\StdoutLoggerFactory; 11 use PHPUnit\Framework\TestCase; 12 13 class FactoryMethodTest extends TestCase 14 { 15 public function testCanCreateStdoutLogging () 16 { 17 $loggerFactory = new StdoutLoggerFactory(); 18 $logger = $loggerFactory -> createLogger (); 19 20 $this -> assertInstanceOf (StdoutLogger :: class , $logger ); 21 } 22 23 public function testCanCreateFileLogging () 24 { 25 $loggerFactory = new FileLoggerFactory( sys_get_temp_dir ()); 26 $logger = $loggerFactory -> createLogger (); 27 28 $this -> assertInstanceOf (FileLogger :: class , $logger ); 29 } 30 } 1.1.4 Объектный пул (Pool) Назначение Порождающий паттерн, который предоставляет набор заранее инициализированных объектов, готовых к использованию («пул»), что не требует каждый раз создавать и уничтожать их. Хранение объектов в пуле может заметно повысить производительность в ситуациях, когда стоимость и скорость инициализации экземпляра класса высоки, а количество одновременно используемых эк- земпляров в любой момент времени является низким. Время на получение объекта из пула легко прогнозируется, тогда как создание нового объекта (особенно с сетевым оверхедом) может занимать неопределенное время. Однако эти преимущества в основном относятся к объектам, которые изначально являются дорого- стоящими по времени создания. Например, соединения с базой данных, соединения сокетов, потоков или инициализация больших графических объектов, таких как шрифты или растровые изображения. В некоторых ситуациях, использование простого пула объектов (которые не зависят от внешних ресурсов, а только занимают память) может оказаться неэффективным и приведёт к снижению производитель- ности. 1.1. Порождающие шаблоны проектирования (Creational) 19 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML 20 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub WorkerPool.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Pool; 6 7 use Countable; 8 9 class WorkerPool implements Countable 10 { 11 /** 12 * @var StringReverseWorker[] 13 */ 14 private array $occupiedWorkers = []; 15 16 /** 17 * @var StringReverseWorker[] 18 */ 19 private array $freeWorkers = []; 20 21 public function get () : StringReverseWorker 22 { 23 if ( count ( $this -> freeWorkers ) === 0 ) { 24 $worker = new StringReverseWorker(); 25 } else { 26 $worker = array_pop ( $this -> freeWorkers ); 27 } 28 29 $this -> occupiedWorkers [ spl_object_hash ( $worker )] = $worker ; 30 31 return $worker ; 32 } 33 34 public function dispose (StringReverseWorker $worker ) : void 35 { 36 $key = spl_object_hash ( $worker ); 37 if ( isset ( $this -> occupiedWorkers [ $key ])) { 38 unset ( $this -> occupiedWorkers [ $key ]); 39 $this -> freeWorkers [ $key ] = $worker ; 40 } 41 } 42 43 public function count () : int 44 { 45 return count ( $this -> occupiedWorkers ) + count ( $this -> freeWorkers ); 46 } 47 } StringReverseWorker.php 1.1. Порождающие шаблоны проектирования (Creational) 21 DesignPatternsPHP Documentation, Выпуск 1.0 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Pool; 6 7 class StringReverseWorker 8 { 9 public function run (string $text ) : string 10 { 11 return strrev ( $text ); 12 } 13 } Тест Tests/PoolTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Pool\Tests; 6 7 use DesignPatterns\Creational\Pool\WorkerPool; 8 use PHPUnit\Framework\TestCase; 9 10 class PoolTest extends TestCase 11 { 12 public function testCanGetNewInstancesWithGet () 13 { 14 $pool = new WorkerPool(); 15 $worker1 = $pool -> get (); 16 $worker2 = $pool -> get (); 17 18 $this -> assertCount ( 2 , $pool ); 19 $this -> assertNotSame ( $worker1 , $worker2 ); 20 } 21 22 public function testCanGetSameInstanceTwiceWhenDisposingItFirst () 23 { 24 $pool = new WorkerPool(); 25 $worker1 = $pool -> get (); 26 $pool -> dispose ( $worker1 ); 27 $worker2 = $pool -> get (); 28 29 $this -> assertCount ( 1 , $pool ); 30 $this -> assertSame ( $worker1 , $worker2 ); 31 } 32 } 22 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 1.1.5 Прототип (Prototype) Назначение Помогает избежать затрат на создание объектов стандартным способом (new Foo()), а вместо этого создаёт прототип и затем клонирует его. Примеры • Большие объемы данных (например, создать 1000000 строк в базе данных сразу через ORM). Диаграмма UML Код Вы можете найти этот код на GitHub BookPrototype.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Prototype; 6 7 abstract class BookPrototype 8 { 9 protected string $title ; 10 protected string $category ; 11 12 abstract public function __clone (); 13 (continues on next page) 1.1. Порождающие шаблоны проектирования (Creational) 23 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 14 final public function getTitle () : string 15 { 16 return $this -> title ; 17 } 18 19 final public function setTitle (string $title ) : void 20 { 21 $this -> title = $title ; 22 } 23 } BarBookPrototype.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Prototype; 6 7 class BarBookPrototype extends BookPrototype 8 { 9 protected string $category = 'Bar' ; 10 11 public function __clone () 12 { 13 } 14 } FooBookPrototype.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Prototype; 6 7 class FooBookPrototype extends BookPrototype 8 { 9 protected string $category = 'Foo' ; 10 11 public function __clone () 12 { 13 } 14 } 24 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Тест Tests/PrototypeTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Prototype\Tests; 6 7 use DesignPatterns\Creational\Prototype\BarBookPrototype; 8 use DesignPatterns\Creational\Prototype\FooBookPrototype; 9 use PHPUnit\Framework\TestCase; 10 11 class PrototypeTest extends TestCase 12 { 13 public function testCanGetFooBook () 14 { 15 $fooPrototype = new FooBookPrototype(); 16 $barPrototype = new BarBookPrototype(); 17 18 for ( $i = 0 ; $i < 10 ; $i ++ ) { 19 $book = clone $fooPrototype ; 20 $book -> setTitle ( 'Foo Book No ' $i ); 21 $this -> assertInstanceOf (FooBookPrototype :: class , $book ); 22 } 23 24 for ( $i = 0 ; $i < 5 ; $i ++ ) { 25 $book = clone $barPrototype ; 26 $book -> setTitle ( 'Bar Book No ' $i ); 27 $this -> assertInstanceOf (BarBookPrototype :: class , $book ); 28 } 29 } 30 } 1.1.6 Простая Фабрика (Simple Factory) Назначение SimpleFactory в примере ниже, это паттерн «Простая Фабрика». Она отличается от Статической Фабрики тем, что собственно не является статической. Таким обра- зом, вы можете иметь множество фабрик с разными параметрами. Простая фабрика всегда должна быть предпочтительнее Статической фабрики! 1.1. Порождающие шаблоны проектирования (Creational) 25 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML Код Вы можете найти этот код на GitHub SimpleFactory.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\SimpleFactory; 6 7 class SimpleFactory 8 { 9 public function createBicycle () : Bicycle 10 { 11 return new Bicycle(); 12 } 13 } Bicycle.php 1 2 3 declare (strict_types = 1 ); 4 (continues on next page) 26 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 5 namespace DesignPatterns\Creational\SimpleFactory; 6 7 class Bicycle 8 { 9 public function driveTo (string $destination ) 10 { 11 } 12 } Usage 1 $factory = new SimpleFactory(); 2 $bicycle = $factory->createBicycle(); 3 $bicycle->driveTo('Paris'); Тест Tests/SimpleFactoryTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\SimpleFactory\Tests; 6 7 use DesignPatterns\Creational\SimpleFactory\Bicycle; 8 use DesignPatterns\Creational\SimpleFactory\SimpleFactory; 9 use PHPUnit\Framework\TestCase; 10 11 class SimpleFactoryTest extends TestCase 12 { 13 public function testCanCreateBicycle () 14 { 15 $bicycle = ( new SimpleFactory()) -> createBicycle (); 16 $this -> assertInstanceOf (Bicycle :: class , $bicycle ); 17 } 18 } 1.1.7 Одиночка (Singleton) Это считается анти-паттерном! Для лучшей тестируемости и сопровождения кода исполь- зуйте Инъекцию Зависимости (Dependency Injection)! 1.1. Порождающие шаблоны проектирования (Creational) 27 DesignPatternsPHP Documentation, Выпуск 1.0 Назначение Позволяет содержать только один экземпляр объекта в приложении, которое будет обрабатывать все обращения, запрещая создавать новый экземпляр. Примеры • DB Connector для подключения к базе данных • Logger • Блокировка файла в приложении (есть только один в файловой системе с одновременным досту- пом к нему) Диаграмма UML Код Вы можете найти этот код на GitHub Singleton.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Singleton; 6 7 use Exception; 8 9 final class Singleton 10 { 11 private static ? Singleton $instance = null ; 12 13 /** 14 * gets the instance via lazy initialization (created on first usage) 15 */ 16 public static function getInstance () : Singleton 17 { 18 if (self :: $instance === null ) { 19 self :: $instance = new self(); (continues on next page) 28 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 20 } 21 22 return self :: $instance ; 23 } 24 25 /** 26 * is not allowed to call from outside to prevent from creating multiple instances, 27 * to use the singleton, you have to obtain the instance from ␣ ˓→ Singleton::getInstance() instead 28 */ 29 private function __construct () 30 { 31 } 32 33 /** 34 * prevent the instance from being cloned (which would create a second instance of ␣ ˓→ it) 35 */ 36 private function __clone () 37 { 38 } 39 40 /** 41 * prevent from being unserialized (which would create a second instance of it) 42 */ 43 public function __wakeup () 44 { 45 throw new Exception( "Cannot unserialize singleton" ); 46 } 47 } Тест Tests/SingletonTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\Singleton\Tests; 6 7 use DesignPatterns\Creational\Singleton\Singleton; 8 use PHPUnit\Framework\TestCase; 9 10 class SingletonTest extends TestCase 11 { 12 public function testUniqueness () 13 { 14 $firstCall = Singleton :: getInstance (); 15 $secondCall = Singleton :: getInstance (); 16 (continues on next page) 1.1. Порождающие шаблоны проектирования (Creational) 29 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 17 $this -> assertInstanceOf (Singleton :: class , $firstCall ); 18 $this -> assertSame ( $firstCall , $secondCall ); 19 } 20 } 1.1.8 Статическая Фабрика (Static Factory) Назначение Подобно AbstractFactory, этот паттерн используется для создания ряда связанных или зависимых объ- ектов. Разница между этим шаблоном и Абстрактной Фабрикой заключается в том, что Статическая Фабрика использует только один статический метод, чтобы создать все допустимые типы объектов. Этот метод, обычно, называется factory или build. Диаграмма UML 30 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub StaticFactory.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\StaticFactory; 6 7 use InvalidArgumentException; 8 9 /** 10 * Note1: Remember, static means global state which is evil because it can't be mocked ␣ ˓→ for tests 11 * Note2: Cannot be subclassed or mock-upped or have multiple different instances. 12 */ 13 final class StaticFactory 14 { 15 public static function factory (string $type ) : Formatter 16 { 17 return match ( $type ) { 18 'number' => new FormatNumber(), 19 'string' => new FormatString(), 20 default => throw new InvalidArgumentException( 'Unknown format given' ), 21 }; 22 } 23 } Formatter.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\StaticFactory; 6 7 interface Formatter 8 { 9 public function format (string $input ) : string; 10 } FormatString.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\StaticFactory; 6 7 class FormatString implements Formatter 8 { (continues on next page) 1.1. Порождающие шаблоны проектирования (Creational) 31 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 9 public function format (string $input ) : string 10 { 11 return $input ; 12 } 13 } FormatNumber.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\StaticFactory; 6 7 class FormatNumber implements Formatter 8 { 9 public function format (string $input ) : string 10 { 11 return number_format ((int) $input ); 12 } 13 } Тест Tests/StaticFactoryTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Creational\StaticFactory\Tests; 6 7 use InvalidArgumentException; 8 use DesignPatterns\Creational\StaticFactory\FormatNumber; 9 use DesignPatterns\Creational\StaticFactory\FormatString; 10 use DesignPatterns\Creational\StaticFactory\StaticFactory; 11 use PHPUnit\Framework\TestCase; 12 13 class StaticFactoryTest extends TestCase 14 { 15 public function testCanCreateNumberFormatter () 16 { 17 $this -> assertInstanceOf (FormatNumber :: class , StaticFactory :: factory ( 'number' )); 18 } 19 20 public function testCanCreateStringFormatter () 21 { 22 $this -> assertInstanceOf (FormatString :: class , StaticFactory :: factory ( 'string' )); 23 } 24 25 public function testException () (continues on next page) 32 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 26 { 27 $this -> expectException (InvalidArgumentException :: class ); 28 29 StaticFactory :: factory ( 'object' ); 30 } 31 } 1.2 Структурные шаблоны проектирования (Structural) При разработке программного обеспечения, Структурные шаблоны проектирования упрощают проек- тирование путем выявления простого способа реализовать отношения между субъектами. 1.2.1 Адаптер (Adapter / Wrapper) Назначение Привести нестандартный или неудобный интерфейс какого-то класса в интерфейс, совместимый с вашим кодом. Адаптер позволяет классам работать вместе стандартным образом, что обычно не полу- чается из-за несовместимых интерфейсов, предоставляя для этого прослойку с интерфейсом, удобным для клиентов, самостоятельно используя оригинальный интерфейс. Примеры • Адаптер клиентских библиотек для работы с базами данных • нормализовать данные нескольких различных веб-сервисов, в одинаковую структуру, как будто вы работаете со стандартным сервисом (например при работе с API соцсетей) 1.2. Структурные шаблоны проектирования (Structural) 33 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML Код Вы можете найти этот код на GitHub Book.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Adapter; 6 7 interface Book 8 { 9 public function turnPage (); 10 11 public function open (); 12 13 public function getPage () : int; 14 } PaperBook.php 34 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Adapter; 6 7 class PaperBook implements Book 8 { 9 private int $page ; 10 11 public function open () : void 12 { 13 $this -> page = 1 ; 14 } 15 16 public function turnPage () : void 17 { 18 $this -> page ++ ; 19 } 20 21 public function getPage () : int 22 { 23 return $this -> page ; 24 } 25 } EBook.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Adapter; 6 7 interface EBook 8 { 9 public function unlock (); 10 11 public function pressNext (); 12 13 /** 14 * returns current page and total number of pages, like [10, 100] is page 10 of 100 15 * 16 * @return int[] 17 */ 18 public function getPage () : array ; 19 } EBookAdapter.php 1 2 3 declare (strict_types = 1 ); (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 35 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 4 5 namespace DesignPatterns\Structural\Adapter; 6 7 /** 8 * This is the adapter here. Notice it implements Book, 9 * therefore you don't have to change the code of the client which is using a Book 10 */ 11 class EBookAdapter implements Book 12 { 13 public function __construct ( protected EBook $eBook ) 14 { 15 } 16 17 /** 18 * This class makes the proper translation from one interface to another. 19 */ 20 public function open () 21 { 22 $this -> eBook -> unlock (); 23 } 24 25 public function turnPage () 26 { 27 $this -> eBook -> pressNext (); 28 } 29 30 /** 31 * notice the adapted behavior here: EBook::getPage() will return two integers, but ␣ ˓→ Book 32 * supports only a current page getter, so we adapt the behavior here 33 */ 34 public function getPage () : int 35 { 36 return $this -> eBook -> getPage ()[ 0 ]; 37 } 38 } Kindle.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Adapter; 6 7 /** 8 * this is the adapted class. In production code, this could be a class from another ␣ ˓→ package, some vendor code. 9 * Notice that it uses another naming scheme and the implementation does something ␣ ˓→ similar but in another way 10 */ 11 class Kindle implements EBook (continues on next page) 36 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 12 { 13 private int $page = 1 ; 14 private int $totalPages = 100 ; 15 16 public function pressNext () 17 { 18 $this -> page ++ ; 19 } 20 21 public function unlock () 22 { 23 } 24 25 /** 26 * returns current page and total number of pages, like [10, 100] is page 10 of 100 27 * 28 * @return int[] 29 */ 30 public function getPage () : array 31 { 32 return [ $this -> page , $this -> totalPages ]; 33 } 34 } Тест Tests/AdapterTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Adapter\Tests; 6 7 use DesignPatterns\Structural\Adapter\PaperBook; 8 use DesignPatterns\Structural\Adapter\EBookAdapter; 9 use DesignPatterns\Structural\Adapter\Kindle; 10 use PHPUnit\Framework\TestCase; 11 12 class AdapterTest extends TestCase 13 { 14 public function testCanTurnPageOnBook () 15 { 16 $book = new PaperBook(); 17 $book -> open (); 18 $book -> turnPage (); 19 20 $this -> assertSame ( 2 , $book -> getPage ()); 21 } 22 23 public function testCanTurnPageOnKindleLikeInANormalBook () (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 37 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 24 { 25 $kindle = new Kindle(); 26 $book = new EBookAdapter( $kindle ); 27 28 $book -> open (); 29 $book -> turnPage (); 30 31 $this -> assertSame ( 2 , $book -> getPage ()); 32 } 33 } 1.2.2 Мост (Bridge) Назначение Отделить абстракцию от её реализации так, что они могут изменяться независимо друг от друга. 38 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML 1.2. Структурные шаблоны проектирования (Structural) 39 DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub Formatter.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Bridge; 6 7 interface Formatter 8 { 9 public function format (string $text ) : string; 10 } PlainTextFormatter.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Bridge; 6 7 class PlainTextFormatter implements Formatter 8 { 9 public function format (string $text ) : string 10 { 11 return $text ; 12 } 13 } HtmlFormatter.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Bridge; 6 7 class HtmlFormatter implements Formatter 8 { 9 public function format (string $text ) : string 10 { 11 return sprintf ( ' %s ' , $text ); 12 } 13 } Service.php 1 2 3 declare (strict_types = 1 ); (continues on next page) 40 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 4 5 namespace DesignPatterns\Structural\Bridge; 6 7 abstract class Service 8 { 9 public function __construct ( protected Formatter $implementation ) 10 { 11 } 12 13 final public function setImplementation (Formatter $printer ) 14 { 15 $this -> implementation = $printer ; 16 } 17 18 abstract public function get () : string; 19 } HelloWorldService.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Bridge; 6 7 class HelloWorldService extends Service 8 { 9 public function get () : string 10 { 11 return $this -> implementation -> format ( 'Hello World' ); 12 } 13 } PingService.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Bridge; 6 7 class PingService extends Service 8 { 9 public function get () : string 10 { 11 return $this -> implementation -> format ( 'pong' ); 12 } 13 } 1.2. Структурные шаблоны проектирования (Structural) 41 DesignPatternsPHP Documentation, Выпуск 1.0 Тест Tests/BridgeTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Bridge\Tests; 6 7 use DesignPatterns\Structural\Bridge\HelloWorldService; 8 use DesignPatterns\Structural\Bridge\HtmlFormatter; 9 use DesignPatterns\Structural\Bridge\PlainTextFormatter; 10 use PHPUnit\Framework\TestCase; 11 12 class BridgeTest extends TestCase 13 { 14 public function testCanPrintUsingThePlainTextFormatter () 15 { 16 $service = new HelloWorldService( new PlainTextFormatter()); 17 18 $this -> assertSame ( 'Hello World' , $service -> get ()); 19 } 20 21 public function testCanPrintUsingTheHtmlFormatter () 22 { 23 $service = new HelloWorldService( new HtmlFormatter()); 24 25 $this -> assertSame ( ' Hello World ' , $service -> get ()); 26 } 27 } 1.2.3 Компоновщик (Composite) Назначение Взаимодействие с иерархической группой объектов также, как и с отдельно взятым экземпляром. 42 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Примеры • Экземпляр класса Form обрабатывает все свои элементы формы, как будто это один экземпляр. И когда вызывается метод render(), он перебирает все дочерние элементы и вызывает их соб- ственный render(). Диаграмма UML Код Вы можете найти этот код на GitHub Renderable.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Composite; 6 7 interface Renderable 8 { 9 public function render () : string; 10 } Form.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Composite; 6 7 /** (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 43 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 8 * The composite node MUST extend the component contract. This is mandatory for building 9 * a tree of components. 10 */ 11 class Form implements Renderable 12 { 13 /** 14 * @var Renderable[] 15 */ 16 private array $elements ; 17 18 /** 19 * runs through all elements and calls render() on them, then returns the complete ␣ ˓→ representation 20 * of the form. 21 * 22 * from the outside, one will not see this and the form will act like a single ␣ ˓→ object instance 23 */ 24 public function render () : string 25 { 26 $formCode = '' ; 33 } 34 35 public function addElement (Renderable $element ) 36 { 37 $this -> elements [] = $element ; 38 } 39 } InputElement.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Composite; 6 7 class InputElement implements Renderable 8 { 9 public function render () : string 10 { 11 return ' |