Патерны программирования. DesignPatternsphp documentation
Скачать 1.98 Mb.
|
Код Вы можете найти этот код на GitHub Facade.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Facade; 6 7 class Facade 8 { 9 public function __construct ( private Bios $bios , private OperatingSystem $os ) (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 59 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 10 { 11 } 12 13 public function turnOn () 14 { 15 $this -> bios -> execute (); 16 $this -> bios -> waitForKeyPress (); 17 $this -> bios -> launch ( $this -> os ); 18 } 19 20 public function turnOff () 21 { 22 $this -> os -> halt (); 23 $this -> bios -> powerDown (); 24 } 25 } OperatingSystem.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Facade; 6 7 interface OperatingSystem 8 { 9 public function halt (); 10 11 public function getName () : string; 12 } Bios.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Facade; 6 7 interface Bios 8 { 9 public function execute (); 10 11 public function waitForKeyPress (); 12 13 public function launch (OperatingSystem $os ); 14 15 public function powerDown (); 16 } 60 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Тест Tests/FacadeTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Facade\Tests; 6 7 use DesignPatterns\Structural\Facade\Bios; 8 use DesignPatterns\Structural\Facade\Facade; 9 use DesignPatterns\Structural\Facade\OperatingSystem; 10 use PHPUnit\Framework\TestCase; 11 12 class FacadeTest extends TestCase 13 { 14 public function testComputerOn () 15 { 16 $os = $this -> createMock (OperatingSystem :: class ); 17 18 $os -> method ( 'getName' ) 19 -> will ( $this -> returnValue ( 'Linux' )); 20 21 $bios = $this -> createMock (Bios :: class ); 22 23 $bios -> method ( 'launch' ) 24 -> with ( $os ); 25 26 /** @noinspection PhpParamsInspection */ 27 $facade = new Facade( $bios , $os ); 28 $facade -> turnOn (); 29 30 $this -> assertSame ( 'Linux' , $os -> getName ()); 31 } 32 } 1.2.8 Текучий Интерфейс (Fluent Interface) Назначение Писать код, который легко читается, как предложения в естественном языке (вроде русского или английского). 1.2. Структурные шаблоны проектирования (Structural) 61 DesignPatternsPHP Documentation, Выпуск 1.0 Примеры • Doctrine2’s QueryBuilder работает примерно также, как пример ниже. • PHPUnit использует текучий интерфейс, чтобы создавать макеты объектов. Диаграмма UML Код Вы можете найти этот код на GitHub Sql.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\FluentInterface; 6 7 class Sql implements \Stringable 8 { 9 private array $fields = []; (continues on next page) 62 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 10 private array $from = []; 11 private array $where = []; 12 13 public function select ( array $fields ) : Sql 14 { 15 $this -> fields = $fields ; 16 17 return $this ; 18 } 19 20 public function from (string $table , string $alias ) : Sql 21 { 22 $this -> from [] = $table ' AS ' $alias ; 23 24 return $this ; 25 } 26 27 public function where (string $condition ) : Sql 28 { 29 $this -> where [] = $condition ; 30 31 return $this ; 32 } 33 34 public function __toString () : string 35 { 36 return sprintf ( 37 'SELECT %s FROM %s WHERE %s' , 38 join ( ', ' , $this -> fields ), 39 join ( ', ' , $this -> from ), 40 join ( ' AND ' , $this -> where ) 41 ); 42 } 43 } Тест Tests/FluentInterfaceTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\FluentInterface\Tests; 6 7 use DesignPatterns\Structural\FluentInterface\Sql; 8 use PHPUnit\Framework\TestCase; 9 10 class FluentInterfaceTest extends TestCase 11 { 12 public function testBuildSQL () (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 63 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 13 { 14 $query = ( new Sql()) 15 -> select ([ 'foo' , 'bar' ]) 16 -> from ( 'foobar' , 'f' ) 17 -> where ( 'f.bar = ?' ); 18 19 $this -> assertSame ( 'SELECT foo, bar FROM foobar AS f WHERE f.bar = ?' , (string) ˓→ $query ); 20 } 21 } 1.2.9 Приспособленец (Flyweight) Назначение Для уменьшения использования памяти Приспособленец разделяет как можно больше памяти между аналогичными объектами. Это необходимо, когда используется большое количество объектов, состо- яние которых не сильно отличается. Обычной практикой является хранение состояния во внешних структурах и передавать их в объект-приспособленец, когда необходимо. 64 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML Код Вы можете найти этот код на GitHub Text.php 1 2 3 declare (strict_types = 1 ); 4 (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 65 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 5 namespace DesignPatterns\Structural\Flyweight; 6 7 /** 8 * This is the interface that all flyweights need to implement 9 */ 10 interface Text 11 { 12 public function render (string $extrinsicState ) : string; 13 } Word.php 1 2 3 namespace DesignPatterns\Structural\Flyweight; 4 5 class Word implements Text 6 { 7 public function __construct ( private string $name ) 8 { 9 } 10 11 public function render (string $extrinsicState ) : string 12 { 13 return sprintf ( 'Word %s with font %s' , $this -> name , $extrinsicState ); 14 } 15 } Character.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Flyweight; 6 7 /** 8 * Implements the flyweight interface and adds storage for intrinsic state, if any. 9 * Instances of concrete flyweights are shared by means of a factory. 10 */ 11 class Character implements Text 12 { 13 /** 14 * Any state stored by the concrete flyweight must be independent of its context. 15 * For flyweights representing characters, this is usually the corresponding ␣ ˓→ character code. 16 */ 17 public function __construct ( private string $name ) 18 { 19 } 20 21 public function render (string $extrinsicState ) : string (continues on next page) 66 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 22 { 23 // Clients supply the context-dependent information that the flyweight needs to ␣ ˓→ draw itself 24 // For flyweights representing characters, extrinsic state usually contains e.g. ˓→ the font. 25 26 return sprintf ( 'Character %s with font %s' , $this -> name , $extrinsicState ); 27 } 28 } TextFactory.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Flyweight; 6 7 use Countable; 8 9 /** 10 * A factory manages shared flyweights. Clients should not instantiate them directly, 11 * but let the factory take care of returning existing objects or creating new ones. 12 */ 13 class TextFactory implements Countable 14 { 15 /** 16 * @var Text[] 17 */ 18 private array $charPool = []; 19 20 public function get (string $name ) : Text 21 { 22 if ( ! isset ( $this -> charPool [ $name ])) { 23 $this -> charPool [ $name ] = $this -> create ( $name ); 24 } 25 26 return $this -> charPool [ $name ]; 27 } 28 29 private function create (string $name ) : Text 30 { 31 if ( strlen ( $name ) == 1 ) { 32 return new Character( $name ); 33 } 34 return new Word( $name ); 35 } 36 37 public function count () : int 38 { 39 return count ( $this -> charPool ); 40 } (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 67 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 41 } Тест Tests/FlyweightTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Flyweight\Tests; 6 7 use DesignPatterns\Structural\Flyweight\TextFactory; 8 use PHPUnit\Framework\TestCase; 9 10 class FlyweightTest extends TestCase 11 { 12 private array $characters = [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 13 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' ]; 14 15 private array $fonts = [ 'Arial' , 'Times New Roman' , 'Verdana' , 'Helvetica' ]; 16 17 public function testFlyweight () 18 { 19 $factory = new TextFactory(); 20 21 for ( $i = 0 ; $i <= 10 ; $i ++ ) { 22 foreach ( $this -> characters as $char ) { 23 foreach ( $this -> fonts as $font ) { 24 $flyweight = $factory -> get ( $char ); 25 $rendered = $flyweight -> render ( $font ); 26 27 $this -> assertSame ( sprintf ( 'Character %s with font %s' , $char , $font ), ˓→ $rendered ); 28 } 29 } 30 } 31 32 foreach ( $this -> fonts as $word ) { 33 $flyweight = $factory -> get ( $word ); 34 $rendered = $flyweight -> render ( 'foobar' ); 35 36 $this -> assertSame ( sprintf ( 'Word %s with font foobar' , $word ), $rendered ); 37 } 38 39 // Flyweight pattern ensures that instances are shared 40 // instead of having hundreds of thousands of individual objects 41 // there must be one instance for every char that has been reused for displaying ␣ ˓→ in different fonts 42 $this -> assertCount ( count ( $this -> characters ) + count ( $this -> fonts ), $factory ); 43 } (continues on next page) 68 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 44 } 1.2.10 Прокси (Proxy) Назначение Создать интерфейс взаимодействия с любым классом, который трудно или невозможно использовать в оригинальном виде. Примеры • Doctrine2 использует прокси для реализации магии фреймворка (например, для ленивой иници- ализации), в то время как пользователь работает со своими собственными классами сущностей и никогда не будет использовать прокси. 1.2. Структурные шаблоны проектирования (Structural) 69 DesignPatternsPHP Documentation, Выпуск 1.0 Диаграмма UML 70 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub BankAccount.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Proxy; 6 7 interface BankAccount 8 { 9 public function deposit (int $amount ); 10 11 public function getBalance () : int; 12 } HeavyBankAccount.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Proxy; 6 7 class HeavyBankAccount implements BankAccount 8 { 9 /** 10 * @var int[] 11 */ 12 private array $transactions = []; 13 14 public function deposit (int $amount ) 15 { 16 $this -> transactions [] = $amount ; 17 } 18 19 public function getBalance () : int 20 { 21 // this is the heavy part, imagine all the transactions even from 22 // years and decades ago must be fetched from a database or web service 23 // and the balance must be calculated from it 24 25 return array_sum ( $this -> transactions ); 26 } 27 } BankAccountProxy.php 1 2 3 declare (strict_types = 1 ); (continues on next page) 1.2. Структурные шаблоны проектирования (Structural) 71 DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 4 5 namespace DesignPatterns\Structural\Proxy; 6 7 class BankAccountProxy extends HeavyBankAccount implements BankAccount 8 { 9 private ? int $balance = null ; 10 11 public function getBalance () : int 12 { 13 // because calculating balance is so expensive, 14 // the usage of BankAccount::getBalance() is delayed until it really is needed 15 // and will not be calculated again for this instance 16 17 if ( $this -> balance === null ) { 18 $this -> balance = parent :: getBalance (); 19 } 20 21 return $this -> balance ; 22 } 23 } Тест ProxyTest.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Proxy\Tests; 6 7 use DesignPatterns\Structural\Proxy\BankAccountProxy; 8 use PHPUnit\Framework\TestCase; 9 10 class ProxyTest extends TestCase 11 { 12 public function testProxyWillOnlyExecuteExpensiveGetBalanceOnce () 13 { 14 $bankAccount = new BankAccountProxy(); 15 $bankAccount -> deposit ( 30 ); 16 17 // this time balance is being calculated 18 $this -> assertSame ( 30 , $bankAccount -> getBalance ()); 19 20 // inheritance allows for BankAccountProxy to behave to an outsider exactly like ␣ ˓→ ServerBankAccount 21 $bankAccount -> deposit ( 50 ); 22 23 // this time the previously calculated balance is returned again without re- ˓→ calculating it 24 $this -> assertSame ( 30 , $bankAccount -> getBalance ()); (continues on next page) 72 Глава 1. Паттерны DesignPatternsPHP Documentation, Выпуск 1.0 (продолжение с предыдущей страницы) 25 } 26 } 1.2.11 Реестр (Registry) Назначение Для реализации централизованного хранения объектов, часто используемых во всем приложении, как правило, реализуется с помощью абстрактного класса только c статическими методами (или с помо- щью шаблона Singleton). Помните, что это вводит глобальное состояние, которого следует избегать. Используйте Dependency Injection вместо Registry. Диаграмма UML 1.2. Структурные шаблоны проектирования (Structural) 73 DesignPatternsPHP Documentation, Выпуск 1.0 Код Вы можете найти этот код на GitHub Registry.php 1 2 3 declare (strict_types = 1 ); 4 5 namespace DesignPatterns\Structural\Registry; 6 7 use InvalidArgumentException; 8 9 abstract class Registry 10 { 11 public const LOGGER = 'logger' ; 12 13 /** 14 * this introduces global state in your application which can not be mocked up for ␣ ˓→ testing 15 * and is therefor considered an anti-pattern! Use dependency injection instead! 16 * 17 * @var Service[] 18 */ 19 private static array $services = []; 20 21 private static array $allowedKeys = [ 22 self :: LOGGER , 23 ]; 24 25 final public static function set (string $key , Service $value ) 26 { 27 if ( ! in_array ( $key , self :: $allowedKeys )) { 28 throw new InvalidArgumentException( 'Invalid key given' ); 29 } 30 31 self :: $services [ $key ] = $value |