Patrones de Diseño PHP
Patrones de Diseño PHP
Versión 1.0
14 de noviembre de 2022
Índice general
1. Patrones 3
1.1. Creacionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.1. Factoría Abstracta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.2. Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.1.3. Factory Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.1.4. Pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.1.5. Prototipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.1.6. Factoría Simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.1.7. Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.1.8. Factoría Estática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.2. Estructurales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.2.1. Adaptador / Wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.2.2. Bridge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
1.2.3. Composite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.2.4. Data Mapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
1.2.5. Decorator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
1.2.6. Inyección de Dependencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
1.2.7. Facade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
1.2.8. Interfaz Fluida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
1.2.9. Flyweight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
1.2.10. Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
1.2.11. Registry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.3. Comportamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1. Chain Of Responsibilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.2. Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.3.3. Interpreter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.3.4. Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.3.5. Mediator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.3.6. Memento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
1.3.7. Null Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
1.3.8. Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
1.3.9. Specification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
1.3.10. State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
1.3.11. Strategy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
1.3.12. Template Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
1.3.13. Visitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
i
1.4. Más . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
1.4.1. Service Locator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
1.4.2. Repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
1.4.3. Entity-Attribute-Value (EAV) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
ii
DesignPatternsPHP Documentation, Versión 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.
El problema con los patrones es que la mayoría de la gente los conoce, pero no sabe cuándo aplicarlos.
Índice general 1
DesignPatternsPHP Documentation, Versión 1.0
2 Índice general
CAPÍTULO 1
Patrones
Los patrones pueden clasificarse en tres categorías diferentes. Por favor pincha en el título de cada pagina de patrón
para ver la explicación completa del patrón en la Wikipedia.
1.1 Creacionales
En ingeniería del software, los patrones de diseño creacionales son patrones que se encargan del los mecanismos de
creación de los objetos, intentando crear objetos de una manera adecuada a cada situación. La forma básica de creación
de objetos podría generar un problema de diseño o añadir complejidad al diseño. Los patrones de diseño creacionales
resuelven este problema controlando de alguna forma la creación de objetos.
Propósito
Para crear una serie de objetos relacionados o dependientes sin especificar a qué clase concreta pertenecen. Normal-
mente las clases creadas implementan las mismas interfaces. El cliente de la factoría abstracta no necesita preocuparse
por cómo estos objetos son creados, él solo sabe qué tiene que hacer con ellos.
3
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
4 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
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 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
5 interface CsvWriter
6 {
7 public function write(array $line): string;
8 }
JsonWriter.php
1 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
5 interface JsonWriter
6 {
7 public function write(array $data, bool $formatted): string;
8 }
UnixCsvWriter.php
1 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
UnixJsonWriter.php
1.1. Creacionales 5
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
11 if ($formatted) {
12 $options = JSON_PRETTY_PRINT;
13 }
14
UnixWriterFactory.php
1 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
WinCsvWriter.php
1 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
WinJsonWriter.php
6 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
11 if ($formatted) {
12 $options = JSON_PRETTY_PRINT;
13 }
14
WinWriterFactory.php
1 <?php
2
3 namespace DesignPatterns\Creational\AbstractFactory;
4
Test
Tests/AbstractFactoryTest.php
1 <?php
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;
(continué en la próxima página)
1.1. Creacionales 7
DesignPatternsPHP Documentation, Versión 1.0
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 Constructor
Propósito
Ejemplos
8 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
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 */
(continué en la próxima página)
1.1. Creacionales 9
DesignPatternsPHP Documentation, Versión 1.0
24 return $builder->getVehicle();
25 }
26 }
Builder.php
1 <?php
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
TruckBuilder.php
1 <?php
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
10 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
CarBuilder.php
1 <?php
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
1.1. Creacionales 11
DesignPatternsPHP Documentation, Versión 1.0
Parts/Vehicle.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Builder\Parts;
6
Parts/Truck.php
1 <?php
2
3 declare(strict_types=1);
4
12 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Parts/Car.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Builder\Parts;
6
Parts/Engine.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Builder\Parts;
6
7 class Engine
8 {
9 }
Parts/Wheel.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Builder\Parts;
6
7 class Wheel
8 {
9 }
Parts/Door.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Builder\Parts;
6
7 class Door
8 {
9 }
1.1. Creacionales 13
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/DirectorTest.php
1 <?php
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
21 $this->assertInstanceOf(Truck::class, $newVehicle);
22 }
23
29 $this->assertInstanceOf(Car::class, $newVehicle);
30 }
31 }
Propósito
La principal ventaja de SimpleFactory es que puedes extender la clase para implementar diferentes formas.
Para casos simples, esta clase abstracta podría ser simplemente una interfaz.
Este es un patrón «real» de diseño porque realiza el Principio de Inversión de Dependencia, también conocido como la
«D» de los principios S.O.L.I.D.
Esto significa que la clase FactoryMethod depende de abstracciones, no de clases concretas. Esto es el truco con respecto
a SimpleFactory o StaticFactory.
14 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
1.1. Creacionales 15
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\FactoryMethod;
6
FileLogger.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\FactoryMethod;
6
LoggerFactory.php
16 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\FactoryMethod;
6
FileLoggerFactory.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\FactoryMethod;
6
1.1. Creacionales 17
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/FactoryMethodTest.php
1 <?php
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
20 $this->assertInstanceOf(StdoutLogger::class, $logger);
21 }
22
28 $this->assertInstanceOf(FileLogger::class, $logger);
29 }
30 }
1.1.4 Pila
Propósito
El Patrón Pila es un patrón de diseño creacional que utiliza un conjunto de objetos inicializados y los mantiene listos
para usar -la «pila»- en lugar de crearlos y destruirlos bajo demanda. Un cliente puede pedirle a la pila un objeto y
realizar las operaciones necesarias sobre él. Cuando el cliente ha terminado devuelve el objeto a la pila en lugar de
destruirlo.
Mantener los objetos en una pila puede ofrecer mejoras significativas de rendimiento en aquellas situaciones donde el
coste de inicializar las instancias es alto, el volumen de veces que se instancia la clase es alto y el número de instancias
que se mantienen en uso a la vez es bajo. El objeto puede recuperarse de la pila en una cantidad de tiempo predecible,
cuando la creación de nuevos objetos (especialmente cuando se realiza a través de una red) puede variar.
Sin embargo, estos beneficios son en su mayoría ciertos para objetos que son costosos con respecto al tiempo, como las
conexiones de base de datos, conexiones de socket, hilos y objetos gráficos grandes como fuentes o mapas de bits. En
algunas situaciones, una pila simple de objetos (que no contienen recursos externos, sino solamente ocupan memoria)
puede no ser eficiente y puede ocasionar una disminución de rendimiento.
18 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
1.1. Creacionales 19
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Pool;
6
7 use Countable;
8
16 /**
17 * @var StringReverseWorker[]
18 */
19 private array $freeWorkers = [];
20
29 $this->occupiedWorkers[spl_object_hash($worker)] = $worker;
30
31 return $worker;
32 }
33
StringReverseWorker.php
20 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
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 }
Test
Tests/PoolTest.php
1 <?php
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
18 $this->assertCount(2, $pool);
19 $this->assertNotSame($worker1, $worker2);
20 }
21
29 $this->assertCount(1, $pool);
30 $this->assertSame($worker1, $worker2);
31 }
32 }
1.1. Creacionales 21
DesignPatternsPHP Documentation, Versión 1.0
1.1.5 Prototipo
Propósito
Para evitar el coste de crear objetos de la forma estándar (new Foo()). En su lugar cree una instancia y clónela.
Ejemplos
Grandes cantidades de datos (ej. crear 1.000.000 de registros en la base de datos a través del ORM).
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Prototype;
6
22 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
BarBookPrototype.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Prototype;
6
FooBookPrototype.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Prototype;
6
1.1. Creacionales 23
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/PrototypeTest.php
1 <?php
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
Propósito
24 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
1.1. Creacionales 25
DesignPatternsPHP Documentation, Versión 1.0
7 class Bicycle
8 {
9 public function driveTo(string $destination)
10 {
11 }
12 }
Usage
Test
Tests/SimpleFactoryTest.php
1 <?php
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
1.1.7 Singleton
26 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Propósito
Tener una única instancia de este objeto en la aplicación que pueda encargarse de todas las llamadas.
Ejemplos
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\Singleton;
6
7 use Exception;
8
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();
20 }
21
1.1. Creacionales 27
DesignPatternsPHP Documentation, Versión 1.0
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 }
Test
Tests/SingletonTest.php
1 <?php
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
17 $this->assertInstanceOf(Singleton::class, $firstCall);
18 $this->assertSame($firstCall, $secondCall);
(continué en la próxima página)
28 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Propósito
Parecido a la factoría abstracta, este patrón es usado para crear conjuntos de objetos relacionados o dependientes. La
diferencia entre este y la factoría abstracta es que el patrón factoría estática usa un sólo método estático para crear todos
los tipos de objetos que puede crear. Este método normalmente se llama factory o build.
Diagrama UML
1.1. Creacionales 29
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
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
Formatter.php
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\StaticFactory;
6
30 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
FormatNumber.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Creational\StaticFactory;
6
Test
Tests/StaticFactoryTest.php
1 <?php
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
1.1. Creacionales 31
DesignPatternsPHP Documentation, Versión 1.0
29 StaticFactory::factory('object');
30 }
31 }
1.2 Estructurales
En Ingeniería de Software, los patrones de diseño estructurales son patrones que facilitan el diseño mostrando una
manera sencilla de realizar relaciones entre entidades.
Propósito
Para traducir una interfaz para una clase en una interfaz compatible. Un adaptador permite que las clases que nor-
malmente no podrían, trabajen juntas. Inicialmente no podrían debido a interfaces incompatibles al proporcionar su
interfaz a los clientes durante el uso de la interfaz original.
Ejemplos
32 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Adapter;
6
7 interface Book
8 {
9 public function turnPage();
10
PaperBook.php
1.2. Estructurales 33
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Adapter;
6
EBook.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Adapter;
6
7 interface EBook
8 {
9 public function unlock();
10
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 <?php
2
3 declare(strict_types=1);
(continué en la próxima página)
34 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
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
30 /**
31 * notice the adapted behavior here: EBook::getPage() will return two integers, but␣
˓→Book
Kindle.php
1 <?php
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
(continué en la próxima página)
1.2. Estructurales 35
DesignPatternsPHP Documentation, Versión 1.0
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 }
Test
Tests/AdapterTest.php
1 <?php
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
20 $this->assertSame(2, $book->getPage());
21 }
22
36 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
28 $book->open();
29 $book->turnPage();
30
31 $this->assertSame(2, $book->getPage());
32 }
33 }
1.2.2 Bridge
Propósito
Separar la abstracción de su implementación para que las dos puedan variar independientemente.
1.2. Estructurales 37
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
38 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Bridge;
6
HtmlFormatter.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Bridge;
6
Service.php
1 <?php
2
3 declare(strict_types=1);
(continué en la próxima página)
1.2. Estructurales 39
DesignPatternsPHP Documentation, Versión 1.0
5 namespace DesignPatterns\Structural\Bridge;
6
HelloWorldService.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Bridge;
6
PingService.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Bridge;
6
40 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/BridgeTest.php
1 <?php
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
1.2.3 Composite
Propósito
1.2. Estructurales 41
DesignPatternsPHP Documentation, Versión 1.0
Ejemplos
Una instancia de una clase formulario maneja todos sus elementos de formulario como una única instancia del
formulario, cuando se llama a render(), se ejecuta a través de todos sus elementos hijo y llama a render() en
ellos
Diagrama UML
Código
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Composite;
6
7 /**
(continué en la próxima página)
42 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
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 = '<form>';
27
InputElement.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Composite;
6
TextElement.php
1.2. Estructurales 43
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Composite;
6
Test
Tests/CompositeTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Composite\Tests;
6
7 use DesignPatterns\Structural\Composite\Form;
8 use DesignPatterns\Structural\Composite\TextElement;
9 use DesignPatterns\Structural\Composite\InputElement;
10 use PHPUnit\Framework\TestCase;
11
27 $this->assertSame(
28 '<form>Email:<input type="text" /><form>Password:<input type="text" /></form>
˓→</form>',
44 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Propósito
Data Mapper, es una Capa de Acceso a Datos que realiza una transferencia bidireccional de datos entre un almacén
de datos persistente (normalmente una base de datos relacional) y una representación de datos en memoria (la capa de
dominio). El objetivo del patrón es mantener la representación en memoria y el almacén de datos persistente separados
entre sí y del propio data mapper. La capa está formada por uno o más mappers (o Capa de Acceso a Datos),realizando la
transferencia de datos. La implementación de mappers varían en alcance. Los mapeadores genéricos manejarán muchos
tipos de entidades de dominio distintas, los mapeadores dedicados manejarán uno o pocos.
El punto clave de este patrón es que, a diferencia del patrón Active Record, el modelo de datos sigue el Principio de
Responsabilidad Única (S.O.L.I.D.).
Ejemplos
1.2. Estructurales 45
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\DataMapper;
6
7 class User
8 {
9 public static function fromState(array $state): User
10 {
(continué en la próxima página)
46 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
UserMapper.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\DataMapper;
6
7 use InvalidArgumentException;
8
9 class UserMapper
10 {
11 public function __construct(private StorageAdapter $adapter)
12 {
13 }
14
15 /**
16 * finds a user from storage based on ID and returns a User object located
17 * in memory. Normally this kind of logic will be implemented using the Repository␣
˓→pattern.
1.2. Estructurales 47
DesignPatternsPHP Documentation, Versión 1.0
29 return $this->mapRowToUser($result);
30 }
31
StorageAdapter.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\DataMapper;
6
7 class StorageAdapter
8 {
9 public function __construct(private array $data)
10 {
11 }
12
13 /**
14 * @return array|null
15 */
16 public function find(int $id)
17 {
18 if (isset($this->data[$id])) {
19 return $this->data[$id];
20 }
21
22 return null;
23 }
24 }
Test
Tests/DataMapperTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\DataMapper\Tests;
6
7 use InvalidArgumentException;
8 use DesignPatterns\Structural\DataMapper\StorageAdapter;
(continué en la próxima página)
48 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
20 $user = $mapper->findById(1);
21
22 $this->assertInstanceOf(User::class, $user);
23 }
24
32 $mapper->findById(1);
33 }
34 }
1.2.5 Decorator
Propósito
Ejemplos
Capa del Servicio Web: Decoradores JSON y XML para un servicio REST (en este caso, solo se debe permitir
uno de estos, por supuesto)
1.2. Estructurales 49
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Decorator;
6
7 interface Booking
8 {
9 public function calculatePrice(): int;
10
BookingDecorator.php
50 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Decorator;
6
DoubleRoomBooking.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Decorator;
6
ExtraBed.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Decorator;
6
1.2. Estructurales 51
DesignPatternsPHP Documentation, Versión 1.0
WiFi.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Decorator;
6
Test
Tests/DecoratorTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Decorator\Tests;
6
7 use DesignPatterns\Structural\Decorator\DoubleRoomBooking;
8 use DesignPatterns\Structural\Decorator\ExtraBed;
9 use DesignPatterns\Structural\Decorator\WiFi;
10 use PHPUnit\Framework\TestCase;
11
18 $this->assertSame(40, $booking->calculatePrice());
19 $this->assertSame('double room', $booking->getDescription());
20 }
(continué en la próxima página)
52 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
27 $this->assertSame(42, $booking->calculatePrice());
28 $this->assertSame('double room with wifi', $booking->getDescription());
29 }
30
37 $this->assertSame(72, $booking->calculatePrice());
38 $this->assertSame('double room with wifi with extra bed', $booking->
˓→getDescription());
39 }
40 }
Propósito
Implementar una arquitectura débilmente acoplada para obtener un código más mantenible, extensible y testeable.
Uso
DatabaseConfiguration se inyecta y DatabaseConnection obtendrá todo lo que necesita de $config. Sin Inyec-
ción de Dependencias, la configuración se crearía directamente en DatabaseConnection, lo que no es muy óptimo
para testear y extender.
Ejemplos
Doctrine2 ORM usa inyección de dependencias e.j. para la configuración que se inyecta en un objeto
Connection. Para propósitos de testing, uno puede fácilmente crear un objeto simulado de la configuración
e inyectarlo en el Objeto Conexión
muchos frameworks ya tienen contenedores para inyección de dependencias que crean objetos a través de una
matriz de configuración y los inyecta donde sea necesario (es decir, en Controladores)
1.2. Estructurales 53
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
54 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\DependencyInjection;
6
7 class DatabaseConfiguration
8 {
9 public function __construct(
10 private string $host,
11 private int $port,
12 private string $username,
13 private string $password
14 ) {
15 }
16
DatabaseConnection.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\DependencyInjection;
6
7 class DatabaseConnection
8 {
9 public function __construct(private DatabaseConfiguration $configuration)
(continué en la próxima página)
1.2. Estructurales 55
DesignPatternsPHP Documentation, Versión 1.0
19 return sprintf(
20 '%s:%s@%s:%d',
21 $this->configuration->getUsername(),
22 $this->configuration->getPassword(),
23 $this->configuration->getHost(),
24 $this->configuration->getPort()
25 );
26 }
27 }
Test
Tests/DependencyInjectionTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\DependencyInjection\Tests;
6
7 use DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration;
8 use DesignPatterns\Structural\DependencyInjection\DatabaseConnection;
9 use PHPUnit\Framework\TestCase;
10
18 $this->assertSame('domnikl:1234@localhost:3306', $connection->getDsn());
19 }
20 }
56 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1.2.7 Facade
Propósito
El objetivo principal del Patrón Facade (o Patrón Fachada) no es evitar que tenga que leer el manual de una compleja
API. Es solo un efecto secundario. El primer objetivo es reducir el acoplamiento y seguir la Ley de Demeter.
Una Fachada está destinada a desacoplar un cliente y un subsistema al incorporar muchas (pero a veces solo una)
interfaz y, por supuesto, reducir la complejidad.
Una fachada no le prohíbe el acceso al subsistema
Puede (debería) tener múltiples fachadas para un subsistema
Por eso una buena fachada no tiene new en ella. Si hay varias creaciones para cada método, no es una fachada, es un
Builder (Constructor) o una [Abstract|Static|Simple] Factory [Method].
La mejor fachada no tiene new y un constructor con parámetros interfaz-type-hinted. Si necesita crear nuevas instancias,
utilice Factory como argumento.
1.2. Estructurales 57
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
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)
(continué en la próxima página)
58 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
OperatingSystem.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Facade;
6
7 interface OperatingSystem
8 {
9 public function halt();
10
Bios.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Facade;
6
7 interface Bios
8 {
9 public function execute();
10
1.2. Estructurales 59
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/FacadeTest.php
1 <?php
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
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
30 $this->assertSame('Linux', $os->getName());
31 }
32 }
Propósito
Escribir código que sea fácil de leer como frases en lenguaje natural (como en castellano).
60 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Ejemplos
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\FluentInterface;
6
1.2. Estructurales 61
DesignPatternsPHP Documentation, Versión 1.0
17 return $this;
18 }
19
24 return $this;
25 }
26
31 return $this;
32 }
33
Test
Tests/FluentInterfaceTest.php
1 <?php
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
62 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1.2.9 Flyweight
Propósito
Para minimizar el uso de la memoria, un Flyweight comparte tanta memoria como sea posible con objetos similares.
Es necesario cuando se utiliza una gran cantidad de objetos que no difieren mucho en el estado. Una práctica común
es mantener el estado en estructuras de datos externas y pasarlas al objeto flyweight cuando se necesite.
1.2. Estructurales 63
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
64 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
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 <?php
2
3 namespace DesignPatterns\Structural\Flyweight;
4
Character.php
1 <?php
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
1.2. Estructurales 65
DesignPatternsPHP Documentation, Versión 1.0
25
TextFactory.php
1 <?php
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
26 return $this->charPool[$name];
27 }
28
66 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/FlyweightTest.php
1 <?php
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
1.2. Estructurales 67
DesignPatternsPHP Documentation, Versión 1.0
1.2.10 Proxy
Propósito
Para interactuar con cualquier cosa que sea costosa o imposible de duplicar.
Ejemplos
Doctrine2 usa proxies para implementar la magia del framework (e.j., inicialización diferida) en ellos, mientras
el usuario trabaja con su propia entidad de clases y nunca usará ni tocará los proxies
68 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
1.2. Estructurales 69
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Proxy;
6
7 interface BankAccount
8 {
9 public function deposit(int $amount);
10
HeavyBankAccount.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Proxy;
6
25 return array_sum($this->transactions);
26 }
27 }
BankAccountProxy.php
1 <?php
2
3 declare(strict_types=1);
(continué en la próxima página)
70 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
5 namespace DesignPatterns\Structural\Proxy;
6
21 return $this->balance;
22 }
23 }
Test
ProxyTest.php
1 <?php
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
23 // this time the previously calculated balance is returned again without re-
˓→ calculating it
24 $this->assertSame(30, $bankAccount->getBalance());
(continué en la próxima página)
1.2. Estructurales 71
DesignPatternsPHP Documentation, Versión 1.0
1.2.11 Registry
Propósito
Implementar un almacenamiento central para los objetos que se utilizan con frecuencia en toda la aplicación, nor-
malmente se implementa utilizando una clase abstracta con solo métodos estáticos (o usando el patrón Singleton).
¡Recuerde que esto introduce un estado global, que debe ser evitado en todo momento. En su lugar, impleméntelo
utilizando la Inyección de Dependencia!
Diagrama UML
72 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Registry;
6
7 use InvalidArgumentException;
8
13 /**
14 * this introduces global state in your application which can not be mocked up for␣
˓→testing
31 self::$services[$key] = $value;
32 }
33
40 return self::$services[$key];
41 }
42 }
Service.php
1 <?php
2
1.2. Estructurales 73
DesignPatternsPHP Documentation, Versión 1.0
5 class Service
6 {
7
8 }
Test
Tests/RegistryTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Structural\Registry\Tests;
6
7 use InvalidArgumentException;
8 use DesignPatterns\Structural\Registry\Registry;
9 use DesignPatterns\Structural\Registry\Service;
10 use PHPUnit\Framework\TestCase;
11
25 $this->assertSame($this->service, Registry::get(Registry::LOGGER));
26 }
27
32 Registry::set('foobar', $this->service);
33 }
34
35 /**
36 * notice @runInSeparateProcess here: without it, a previous test might have set it␣
˓→already and
37 * testing would not be possible. That's why you should implement Dependency␣
˓→Injection where an
74 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
46 Registry::get(Registry::LOGGER);
47 }
48 }
1.3 Comportamiento
En ingeniería de software, los patrones de diseño de comportamiento son patrones queidentifican patrones de comunica-
ción comunes entre objetos y los entienden. Al hacerlo, estos patrones aumentan la flexibilidad a la hora de realizaresta
comunicación.
Purpose
Usado para construir una cadena de objetos que permita manejar una llamada en orden secuencial. Si un objeto no
puede manejar una llamada, delega la llamada al siguiente en la cadena, etc.
Examples
un logging framework, donde cada elemento de la cadena decide de forma autónoma qué hacer con un mensaje
de log
un filtro de Spam
Cachear: el primer objeto es una instancia de, por ejemplo, una interfaz Memcached,si es que » falla » delega la
llamada a la interfaz de la base de datos
1.3. Comportamiento 75
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\ChainOfResponsibilities;
6
7 use Psr\Http\Message\RequestInterface;
8
15 /**
16 * This approach by using a template method pattern ensures you that
17 * each subclass will not forget to call the successor
18 */
19 final public function handle(RequestInterface $request): ?string
20 {
21 $processed = $this->processing($request);
22
76 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
28 return $processed;
29 }
30
Responsible/FastStorage.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;
6
7 use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;
8 use Psr\Http\Message\RequestInterface;
9
29 return null;
30 }
31 }
Responsible/SlowStorage.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;
6
7 use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;
8 use Psr\Http\Message\RequestInterface;
9
1.3. Comportamiento 77
DesignPatternsPHP Documentation, Versión 1.0
15
Test
Tests/ChainTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Tests;
6
7 use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;
8 use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\
˓→HttpInMemoryCacheHandler;
9 use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowDatabaseHandler;
10 use PHPUnit\Framework\TestCase;
11 use Psr\Http\Message\RequestInterface;
12 use Psr\Http\Message\UriInterface;
13
32 $request = $this->createMock(RequestInterface::class);
33 $request->method('getMethod')
34 ->willReturn('GET');
35 $request->method('getUri')->willReturn($uri);
(continué en la próxima página)
78 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
46 $request = $this->createMock(RequestInterface::class);
47 $request->method('getMethod')
48 ->willReturn('GET');
49 $request->method('getUri')->willReturn($uri);
50
1.3.2 Command
Propósito
Ejemplos
A text editor : all events are commands which can be undone, stacked and saved.
Las grandes herramientas CLI utilizan subcomandos para distribuir varias tareas y empaquetarlas en » módulos
«, cada uno de estos se puede implementar con el patrón Command (por ejemplo, vagrant)
1.3. Comportamiento 79
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Command;
6
7 interface Command
(continué en la próxima página)
80 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
UndoableCommand.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Command;
6
HelloCommand.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Command;
6
7 /**
8 * This concrete command calls "print" on the Receiver, but an external
9 * invoker just knows that it can call "execute"
10 */
11 class HelloCommand implements Command
12 {
13 /**
14 * Each concrete command is built with different receivers.
15 * There can be one, many or completely no receivers, but there can be other␣
˓→commands in the parameters
16 */
17 public function __construct(private Receiver $output)
18 {
19 }
20
21 /**
22 * execute and output "Hello World".
23 */
24 public function execute()
25 {
(continué en la próxima página)
1.3. Comportamiento 81
DesignPatternsPHP Documentation, Versión 1.0
AddMessageDateCommand.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Command;
6
7 /**
8 * This concrete command tweaks receiver to add current date to messages
9 * invoker just knows that it can call "execute"
10 */
11 class AddMessageDateCommand implements UndoableCommand
12 {
13 /**
14 * Each concrete command is built with different receivers.
15 * There can be one, many or completely no receivers, but there can be other␣
˓→commands in the parameters.
16 */
17 public function __construct(private Receiver $output)
18 {
19 }
20
21 /**
22 * Execute and make receiver to enable displaying messages date.
23 */
24 public function execute()
25 {
26 // sometimes, there is no receiver and this is the command which
27 // does all the work
28 $this->output->enableDate();
29 }
30
31 /**
32 * Undo the command and make receiver to disable displaying messages date.
33 */
34 public function undo()
35 {
36 // sometimes, there is no receiver and this is the command which
37 // does all the work
38 $this->output->disableDate();
39 }
40 }
Receiver.php
82 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Command;
6
7 /**
8 * Receiver is a specific service with its own contract and can be only concrete.
9 */
10 class Receiver
11 {
12 private bool $enableDate = false;
13
14 /**
15 * @var string[]
16 */
17 private array $output = [];
18
25 $this->output[] = $str;
26 }
27
33 /**
34 * Enable receiver to display message date
35 */
36 public function enableDate()
37 {
38 $this->enableDate = true;
39 }
40
41 /**
42 * Disable receiver to display message date
43 */
44 public function disableDate()
45 {
46 $this->enableDate = false;
47 }
48 }
Invoker.php
1 <?php
2
1.3. Comportamiento 83
DesignPatternsPHP Documentation, Versión 1.0
5 namespace DesignPatterns\Behavioral\Command;
6
7 /**
8 * Invoker is using the command given to it.
9 * Example : an Application in SF2.
10 */
11 class Invoker
12 {
13 private Command $command;
14
15 /**
16 * in the invoker we find this kind of method for subscribing the command
17 * There can be also a stack, a list, a fixed set ...
18 */
19 public function setCommand(Command $cmd)
20 {
21 $this->command = $cmd;
22 }
23
24 /**
25 * executes the command; the invoker is the same whatever is the command
26 */
27 public function run()
28 {
29 $this->command->execute();
30 }
31 }
Test
Tests/CommandTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Command\Tests;
6
7 use DesignPatterns\Behavioral\Command\HelloCommand;
8 use DesignPatterns\Behavioral\Command\Invoker;
9 use DesignPatterns\Behavioral\Command\Receiver;
10 use PHPUnit\Framework\TestCase;
11
84 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
19 $invoker->setCommand(new HelloCommand($receiver));
20 $invoker->run();
21 $this->assertSame('Hello World', $receiver->getOutput());
22 }
23 }
Tests/UndoableCommandTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Command\Tests;
6
7 use DesignPatterns\Behavioral\Command\AddMessageDateCommand;
8 use DesignPatterns\Behavioral\Command\HelloCommand;
9 use DesignPatterns\Behavioral\Command\Invoker;
10 use DesignPatterns\Behavioral\Command\Receiver;
11 use PHPUnit\Framework\TestCase;
12
20 $invoker->setCommand(new HelloCommand($receiver));
21 $invoker->run();
22 $this->assertSame('Hello World', $receiver->getOutput());
23
27 $invoker->run();
28 $this->assertSame("Hello World\nHello World [" . date('Y-m-d') . ']', $receiver->
˓→getOutput());
29
30 $messageDateCommand->undo();
31
32 $invoker->run();
33 $this->assertSame("Hello World\nHello World [" . date('Y-m-d') . "]\nHello World
˓→", $receiver->getOutput());
34 }
35 }
1.3. Comportamiento 85
DesignPatternsPHP Documentation, Versión 1.0
1.3.3 Interpreter
Purpose
For a given language, it defines the representation of its grammar as «No Terminal Expression» and «Terminal Expres-
sion», as well as an interpreter for the sentences of that language.
Examples
An example of a binary logic interpreter, each definition is defined by its own class
UML Diagram
Code
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Interpreter;
6
86 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Context.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Interpreter;
6
7 use Exception;
8
9 class Context
10 {
11 private array $poolVariable;
12
19 return $this->poolVariable[$name];
20 }
21
VariableExp.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Interpreter;
6
7 /**
8 * This TerminalExpression
9 */
10 class VariableExp extends AbstractExp
11 {
12 public function __construct(private string $name)
13 {
14 }
15
1.3. Comportamiento 87
DesignPatternsPHP Documentation, Versión 1.0
AndExp.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Interpreter;
6
7 /**
8 * This NoTerminalExpression
9 */
10 class AndExp extends AbstractExp
11 {
12 public function __construct(private AbstractExp $first, private AbstractExp $second)
13 {
14 }
15
OrExp.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Interpreter;
6
7 /**
8 * This NoTerminalExpression
9 */
10 class OrExp extends AbstractExp
11 {
12 public function __construct(private AbstractExp $first, private AbstractExp $second)
13 {
14 }
15
88 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/InterpreterTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Interpreter\Tests;
6
7 use DesignPatterns\Behavioral\Interpreter\AndExp;
8 use DesignPatterns\Behavioral\Interpreter\Context;
9 use DesignPatterns\Behavioral\Interpreter\OrExp;
10 use DesignPatterns\Behavioral\Interpreter\VariableExp;
11 use PHPUnit\Framework\TestCase;
12
34 // A B
35 $exp1 = new OrExp($this->a, $this->b);
36 $result1 = $exp1->interpret($this->context);
37
40 // $exp1 C
41 $exp2 = new OrExp($exp1, $this->c);
42 $result2 = $exp2->interpret($this->context);
43
1.3. Comportamiento 89
DesignPatternsPHP Documentation, Versión 1.0
53 // A B
54 $exp1 = new AndExp($this->a, $this->b);
55 $result1 = $exp1->interpret($this->context);
56
59 // $exp1 C
60 $exp2 = new AndExp($exp1, $this->c);
61 $result2 = $exp2->interpret($this->context);
62
1.3.4 Iterator
Propósito
Ejemplos
para procesar un archivo línea por línea simplemente ejecutando todas las líneas (que tienen una representación
de un objeto) para un archivo (que, por supuesto, también es un objeto)
Nota
La Standard PHP Library (SPL) define un iterador de interfaz que es más adecuado para esto. Con frecuencia deseará
implementar la interfaz Countable también, para permitir count($object) en su objeto iterable
90 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Diarama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Iterator;
6
7 class Book
8 {
9 public function __construct(private string $title, private string $author)
10 {
11 }
12
1.3. Comportamiento 91
DesignPatternsPHP Documentation, Versión 1.0
BookList.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Iterator;
6
7 use Countable;
8 use Iterator;
9
31 $this->books = array_values($this->books);
32 }
33
92 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Test
Tests/IteratorTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Iterator\Tests;
6
7 use DesignPatterns\Behavioral\Iterator\Book;
8 use DesignPatterns\Behavioral\Iterator\BookList;
9 use PHPUnit\Framework\TestCase;
10
20 $books = [];
21
1.3. Comportamiento 93
DesignPatternsPHP Documentation, Versión 1.0
26 $this->assertSame(
27 [
28 'Learning PHP Design Patterns by William Sanders',
29 'Professional Php Design Patterns by Aaron Saray',
30 'Clean Code by Robert C. Martin',
31 ],
32 $books
33 );
34 }
35
46 $books = [];
47 foreach ($bookList as $book) {
48 $books[] = $book->getAuthorAndTitle();
49 }
50
51 $this->assertSame(
52 ['Professional Php Design Patterns by Aaron Saray'],
53 $books
54 );
55 }
56
64 $this->assertCount(1, $bookList);
65 }
66
75 $this->assertCount(0, $bookList);
(continué en la próxima página)
94 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
1.3.5 Mediator
Propósito
Este patrón proporciona una forma sencilla de desacoplar muchos componentes que funcionan conjuntamente. Es una
buena alternativa a Observer SI tiene una «inteligencia central» como un controlador (pero no en el sentido de MVC).
Todos los componentes (llamados Colleague) solo están acoplados a la interfaz del Mediator y es algo bueno porque
en POO, un buen amigo es mejor que muchos. Esta es la característica clave de este patrón.
1.3. Comportamiento 95
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
96 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
Código
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Mediator;
6
7 interface Mediator
8 {
9 public function getUser(string $username): string;
10 }
Colleague.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Mediator;
6
Ui.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Mediator;
6
UserRepository.php
1 <?php
2
(continué en la próxima página)
1.3. Comportamiento 97
DesignPatternsPHP Documentation, Versión 1.0
5 namespace DesignPatterns\Behavioral\Mediator;
6
UserRepositoryUiMediator.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Mediator;
6
Test
Tests/MediatorTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Tests\Mediator\Tests;
6
7 use DesignPatterns\Behavioral\Mediator\Ui;
8 use DesignPatterns\Behavioral\Mediator\UserRepository;
(continué en la próxima página)
98 Capítulo 1. Patrones
DesignPatternsPHP Documentation, Versión 1.0
18 $this->expectOutputString('User: Dominik');
19 $mediator->printInfoAbout('Dominik');
20 }
21 }
1.3.6 Memento
Propósito
It provides the ability to restore an object to it’s previous state (undo via rollback) or to gain access to state of the object,
without revealing it’s implementation (i.e., the object is not required to have a function to return the current state).
El patrón Memento se implementa con tres objetos: el Originador, el Conserje y el Memento.
Memento - un objeto que contiene una instantánea concreta y única del estado*de cualquier objeto o recurso: cadena,
número, array, una instancia de clasey así sucesivamente. La singularidad en este caso no implica la prohibición de
la existencia de estados similares en diferentes instantáneas. Eso significa que el estado se puede extraer como clon
independiente. Cualquier objeto almacenado en Memento debe ser *una copia completa del objeto original en lugar de
unreferencia al objeto original. El objeto Memento es un «objeto opaco» (el objeto que nadie puede ni debe cambiar).
Originador: es un objeto que contiene el estado actual de un objeto externo de tipo estrictamente especificado. El
originador puede crear una copia única de este estado y la devuelve envuelta en un Memento. El Originador no conoce
el historial de cambios. Puede establecer un estado concreto para el Originador desde el exterior, que se considerará
como actual. El Originador debe asegurar que el estado dado corresponda al tipo de objeto permitido. El originador
puede (pero no debería) tener cualquier método, pero no pueden realizar cambios en el estado del objeto guardado.
El Conserje controla el historial de estados. Puede realizar cambios en un objeto; tomar la decisión de guardar el estado
de un objeto externo en el Originador; preguntar desde la instantánea del Originador del estado actual; o establecer el
Estado del Originador a la equivalencia con alguna instantánea de la historia.
Ejemplos
1.3. Comportamiento 99
DesignPatternsPHP Documentation, Versión 1.0
Diagrama UML
Código
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Memento;
(continué en la próxima página)
7 class Memento
8 {
9 public function __construct(private State $state)
10 {
11 }
12
State.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Memento;
6
7 use InvalidArgumentException;
8
18 /**
19 * @var string[]
20 */
21 private static array $validStates = [
22 self::STATE_CREATED,
23 self::STATE_OPENED,
24 self::STATE_ASSIGNED,
25 self::STATE_CLOSED,
26 ];
27
32 $this->state = $state;
33 }
34
Ticket.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Memento;
6
7 /**
8 * Ticket is the "Originator" in this implementation
9 */
10 class Ticket
11 {
12 private State $currentState;
13
Test
Tests/MementoTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Memento\Tests;
6
7 use DesignPatterns\Behavioral\Memento\State;
8 use DesignPatterns\Behavioral\Memento\Ticket;
9 use PHPUnit\Framework\TestCase;
10
22 $memento = $ticket->saveToMemento();
23
28 // now restore to the opened state, but verify that the state object has been␣
˓→ cloned for the memento
29 $ticket->restoreFromMemento($memento);
30
Propósito
NullObject no es un patrón GoF pero es un esquema bastante frecuente como para ser considerado patrón. Tiene los
siguientes beneficios:
Código cliente simplificado
Reduce las posibilidades de excepciones null pointer
Menos condicionales requieren menos tests
Métodos que devuelven un objeto o un valor nulo deben devolver un objeto o NullObject.
NullObjectsimplificar el código repetitivo como if (!is_null($obj)) { $obj->callSomething(); } a
solo $obj->callSomething(); eliminando la verificación condicional en el código cliente.
Ejemplos
Null logger o salida null para preservar una forma estándar de interacción entre objetos, incluso si este no debiera
hacer nada
manejador null en un patrón Chain of Responsibilities
comando null en un patrón Command
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\NullObject;
6
7 class Service
8 {
9 public function __construct(private Logger $logger)
10 {
11 }
12
13 /**
14 * do something ...
15 */
16 public function doSomething()
17 {
18 // notice here that you don't have to check if the logger is set with eg. is_
˓→null(), instead just use it
Logger.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\NullObject;
6
7 /**
8 * Key feature: NullLogger must inherit from this interface like any other loggers
9 */
10 interface Logger
11 {
12 public function log(string $str);
13 }
PrintLogger.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\NullObject;
6
NullLogger.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\NullObject;
6
Test
Tests/LoggerTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\NullObject\Tests;
6
7 use DesignPatterns\Behavioral\NullObject\NullLogger;
8 use DesignPatterns\Behavioral\NullObject\PrintLogger;
9 use DesignPatterns\Behavioral\NullObject\Service;
10 use PHPUnit\Framework\TestCase;
11
1.3.8 Observer
Propósito
Para implementar un comportamiento de publicación / suscripción a un objeto, siempre que un objeto «Sujeto» cambia
su estado, los «Observadores» adjuntos serán avisados. Se utiliza para acortar la cantidad de objetos acoplados y utiliza
un loose coupling en su lugar.
Ejemplos
Se observa un sistema de cola de mensajes para mostrar el progreso de un trabajo en una GUI
Nota
PHP ya define dos interfaces que pueden ayudar a implementar este patrón: SplObserver y SplSubject.
Diagrama UML
Código
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Observer;
6
7 use SplSubject;
8 use SplObjectStorage;
9 use SplObserver;
10
11 /**
12 * User implements the observed object (called Subject), it maintains a list of␣
˓→observers and sends notifications to
UserObserver.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Observer;
6
7 use SplObserver;
8 use SplSubject;
9
17 /**
18 * It is called by the Subject, usually by SplSubject::notify()
19 */
20 public function update(SplSubject $subject): void
21 {
22 $this->changedUsers[] = clone $subject;
23 }
24
25 /**
26 * @return SplSubject[]
27 */
28 public function getChangedUsers(): array
29 {
30 return $this->changedUsers;
31 }
32 }
Test
Tests/ObserverTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Observer\Tests;
6
7 use DesignPatterns\Behavioral\Observer\User;
8 use DesignPatterns\Behavioral\Observer\UserObserver;
9 use PHPUnit\Framework\TestCase;
10
20 $user->changeEmail('[email protected]');
21 $this->assertCount(1, $observer->getChangedUsers());
22 }
23 }
1.3.9 Specification
Propósito
Crea una especificación clara de las reglas comerciales, donde los objetos se pueden comparar. La clase de especifica-
ción compuesta tiene un método llamado isSatisfiedBy que devuelve verdadero o falso dependiendo de si el objeto
dado satisface la especificación.
Ejemplos
RulerZ
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Specification;
6
7 class Item
8 {
9 public function __construct(private float $price)
10 {
11 }
12
Specification.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Specification;
6
7 interface Specification
8 {
9 public function isSatisfiedBy(Item $item): bool;
10 }
OrSpecification.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Specification;
6
14 /**
15 * @param Specification[] $specifications
(continué en la próxima página)
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Specification;
6
23 return true;
24 }
25 }
AndSpecification.php
1 <?php
(continué en la próxima página)
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Specification;
6
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Specification;
6
Test
Tests/SpecificationTest.php
1 <?php
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
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
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
46 $this->assertTrue($notSpec->isSatisfiedBy(new Item(150)));
47 $this->assertFalse($notSpec->isSatisfiedBy(new Item(50)));
48 }
49 }
1.3.10 State
Propósito
Encapsula el comportamiento variable de la misma rutina en función del estado de un objeto. Esta puede ser una forma
más limpia para que un objeto cambie su comportamiento en tiempo de ejecución sin tener que recurrir a declaraciones
condicionales monolíticas de gran tamaño
Diagrama UML
Código
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\State;
6
7 class OrderContext
8 {
9 private State $state;
10
16 return $order;
17 }
18
State.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\State;
6
7 interface State
8 {
9 public function proceedToNext(OrderContext $context);
10
StateCreated.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\State;
6
StateShipped.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\State;
6
StateDone.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\State;
6
Test
Tests/StateTest.php
1 <?php
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
16 $this->assertSame('created', $orderContext->toString());
17 }
18
24 $this->assertSame('shipped', $contextOrder->toString());
25 }
26
33 $this->assertSame('done', $contextOrder->toString());
34 }
35
43 $this->assertSame('done', $contextOrder->toString());
44 }
45 }
1.3.11 Strategy
Terminología
Contexto
Estrategia
Estrategia Concreta
Propósito
Para separar estrategias y permitir un cambio rápido entre ellas. Además, este patrón es una buena alternativa a la
herencia (en lugar de tener una clase abstracta que se extiende).
Ejemplos
ordenar una lista de objetos, una estrategia por fecha, la otra por id
simplificar las pruebas unitarias: p. ej. cambiar entre archivo y almacenamiento en memoria
Diagrama UML
Código
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Strategy;
(continué en la próxima página)
7 class Context
8 {
9 public function __construct(private Comparator $comparator)
10 {
11 }
12
17 return $elements;
18 }
19 }
Comparator.php
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Strategy;
6
7 use DateTime;
8
IdComparator.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Strategy;
6
Test
Tests/StrategyTest.php
1 <?php
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
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 }
Propósito
Diagrama UML
Código
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\TemplateMethod;
6
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
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
58 /**
59 * @return string[]
60 */
61 final public function getThingsToDo(): array
62 {
63 return $this->thingsToDo;
64 }
65 }
BeachJourney.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\TemplateMethod;
6
CityJourney.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\TemplateMethod;
6
Test
Tests/JourneyTest.php
1 <?php
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
18 $this->assertSame(
19 ['Buy a flight ticket', 'Taking the plane', 'Swimming and sun-bathing',
˓→'Taking the plane'],
20 $beachJourney->getThingsToDo()
21 );
22 }
23
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.13 Visitor
Propósito
El patrón Visitor le permite subcontratar las operaciones en objetos a otros objetos. La razón principal para hacerlo es
mantener una separación de cometidos. Pero las clases tienen que definir un contrato para permitir a los visitantes(el
método Role::accept es un ejemplo).
El contrato es una clase abstracta, pero también puede tener una interfaz limpia. En ese caso, cada Visitante tiene que
elegir por sí mismo qué método invocar en el visitante.
Diagrama UML
Código
1 <?php
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
RecordingVisitor.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Visitor;
6
24 /**
25 * @return Role[]
26 */
27 public function getVisited(): array
28 {
29 return $this->visited;
(continué en la próxima página)
Role.php
1 <?php
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 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Visitor;
6
Group.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\Behavioral\Visitor;
6
Test
Tests/VisitorTest.php
1 <?php
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
31 /**
32 * @dataProvider provideRoles
33 */
34 public function testVisitSomeRole(Role $role)
(continué en la próxima página)
1.4 Más
Propósito
Implementar una arquitectura débilmente acoplada con el fin de obtener un código más testeable, mantenible y amplia-
ble. El patrón DI y el patrón Service Locator son una implementación del patrón Inverse of Control.
Uso
Con ``ServiceLocator”” puede registrar un servicio para una interfaz determinada. Al usar la interfaz, puede recuperar
el servicio y usarlo en las clases de la aplicación sin conocer su implementación. Puede configurar e inyectar el objeto
Service Locator en el arranque.
Diagrama UML
Código
1 <?php
2
3 namespace DesignPatterns\More\ServiceLocator;
4
5 interface Service
6 {
7
8 }
ServiceLocator.php
1 <?php
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
35 }
36
47 }
48
49 $this->instantiated[$class] = $object;
50
51 return $object;
52 }
53 }
LogService.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\ServiceLocator;
6
10 }
Test
Tests/ServiceLocatorTest.php
1 <?php
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
24 $this->assertTrue($this->serviceLocator->has(LogService::class));
25 $this->assertFalse($this->serviceLocator->has(self::class));
26 }
27
33 $this->assertInstanceOf(LogService::class, $logger);
34 }
35 }
1.4.2 Repositorio
Propósito
Media entre el dominio y las capas de mapeo de datos utilizando una interfaz similar a una colección para acceder
a los objetos del dominio. El repositorio encapsula el conjunto de objetos persistentes en un almacén de datos y las
operaciones que se realizan sobre ellos, lo que proporciona una vista más orientada a objetos de la capa de persistencia.
El repositorio también respalda el objetivo de lograr una separación limpia y una dependencia unidireccional entre el
dominio y las capas de mapeo de datos.
Ejemplos
Doctrine 2 ORM: hay un Repositorio que media entre Entity y DBAL y contiene métodos para recuperar objetos
Framework Laravel
Diagrama UML
Código
1 <?php
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 {
(continué en la próxima página)
PostId.php
1 <?php
2
3 declare(strict_types=1);
(continué en la próxima página)
5 namespace DesignPatterns\More\Repository\Domain;
6
7 use InvalidArgumentException;
8
9 /**
10 * This is a perfect example of a value object that is identifiable by it's value alone␣
˓→and
12 * is immutability.
13 *
14 * Notice also the use of a named constructor (fromInt) which adds a little context when␣
˓→creating an instance.
15 */
16 class PostId
17 {
18 public static function fromInt(int $id): PostId
19 {
20 self::ensureIsValid($id);
21
PostStatus.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\Repository\Domain;
6
7 use InvalidArgumentException;
8
9 /**
(continué en la próxima página)
11 * either from a string or int and is able to validate itself. An instance can then be␣
˓→converted back to int or string.
12 */
13 class PostStatus
14 {
15 public const STATE_DRAFT_ID = 1;
16 public const STATE_PUBLISHED_ID = 2;
17
54 /**
55 * there is a reason that I avoid using __toString() as it operates outside of the␣
˓→stack in PHP
70
PostRepository.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\Repository;
6
7 use OutOfBoundsException;
8 use DesignPatterns\More\Repository\Domain\Post;
9 use DesignPatterns\More\Repository\Domain\PostId;
10
11 /**
12 * This class is situated between Entity layer (class Post) and access object layer␣
˓→(Persistence).
13 *
14 * Repository encapsulates the set of objects persisted in a data store and the␣
˓→operations performed over them
37 }
38
39 return Post::fromState($arrayData);
40 }
41
Persistence.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\Repository;
6
7 interface Persistence
8 {
9 public function generateId(): int;
10
InMemoryPersistence.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\Repository;
(continué en la próxima página)
7 use OutOfBoundsException;
8
18 return $this->lastId;
19 }
20
32 return $this->data[$id];
33 }
34
41 unset($this->data[$id]);
42 }
43 }
Test
Tests/PostRepositoryTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\Repository\Tests;
6
7 use OutOfBoundsException;
8 use DesignPatterns\More\Repository\Domain\PostId;
(continué en la próxima página)
34 $this->repository->findById(PostId::fromInt(42));
35 }
36
43 $this->repository->findById($postId);
44
45 $this->assertEquals($postId, $this->repository->findById($postId)->getId());
46 $this->assertEquals(PostStatus::STATE_DRAFT, $post->getStatus()->toString());
47 }
48 }
The Entity–attribute–value (EAV) pattern in order to implement EAV model with PHP.
Purpose
The Entity–attribute–value (EAV) model is a data model to describe entities where the number of attributes (properties,
parameters) that can be used to describe them is potentially vast, but the number that will actually apply to a given entity
is relatively modest.
UML Diagram
Code
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\EAV;
6
7 use SplObjectStorage;
8
16 /**
17 * @param Value[] $values
18 */
19 public function __construct(private string $name, array $values)
20 {
21 $this->values = new SplObjectStorage();
22
Attribute.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\EAV;
6
7 use SplObjectStorage;
(continué en la próxima página)
Value.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\EAV;
6
Test
Tests/EAVTest.php
1 <?php
2
3 declare(strict_types=1);
4
5 namespace DesignPatterns\More\EAV\Tests;
6
7 use DesignPatterns\More\EAV\Attribute;
8 use DesignPatterns\More\EAV\Entity;
9 use DesignPatterns\More\EAV\Value;
10 use PHPUnit\Framework\TestCase;
11