DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un...

139
DesignPatternsPHP Documentation Versión 1.0 Dominik Liebler and contributors 04 de noviembre de 2020

Transcript of DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un...

Page 1: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP DocumentationVersión 1.0

Dominik Liebler and contributors

04 de noviembre de 2020

Page 2: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos
Page 3: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

Índice general

1. Patterns 31.1. Creational . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.1.1. Abstract Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.1.2. Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.1.3. Factory Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141.1.4. Pool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181.1.5. Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.1.6. Simple Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241.1.7. Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261.1.8. Static Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

1.2. Structural . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311.2.1. Adapter / Wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311.2.2. Bridge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351.2.3. Composite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391.2.4. Data Mapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421.2.5. Decorator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461.2.6. Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501.2.7. Facade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531.2.8. Fluent Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561.2.9. Flyweight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591.2.10. Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621.2.11. Registry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

1.3. Behavioral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.1. Chain Of Responsibilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.2. Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731.3.3. Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.3.4. Mediator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831.3.5. Memento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871.3.6. Null Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 911.3.7. Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951.3.8. Specification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 981.3.9. State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1031.3.10. Strategy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1061.3.11. Template Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101.3.12. Visitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

1.4. More . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

I

Page 4: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

1.4.1. Service Locator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1181.4.2. Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1231.4.3. Entity-Attribute-Value (EAV) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

II

Page 5: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

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 patternhas a small list of examples.

I think the problem with patterns is that often people do know them but don’t know when to apply which.

Índice general 1

Page 6: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

2 Índice general

Page 7: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

CAPÍTULO 1

Patterns

The patterns can be structured in roughly three different categories. Please click on the title of every pattern’s pagefor a full explanation of the pattern on Wikipedia.

1.1 Creational

In software engineering, creational design patterns are design patterns that deal with object creation mechanisms,trying to create objects in a manner suitable to the situation. The basic form of object creation could result in designproblems or added complexity to the design. Creational design patterns solve this problem by somehow controllingthis object creation.

1.1.1 Abstract Factory

Purpose

To create series of related or dependent objects without specifying their concrete classes. Usually the created classesall implement the same interface. The client of the abstract factory does not care about how these objects are created,it just knows how they go together.

3

Page 8: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

4 Capítulo 1. Patterns

Page 9: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

WriterFactory.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 interface WriterFactory6 {7 public function createCsvWriter(): CsvWriter;8 public function createJsonWriter(): JsonWriter;9 }

CsvWriter.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 interface CsvWriter6 {7 public function write(array $line): string;8 }

JsonWriter.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 interface JsonWriter6 {7 public function write(array $data, bool $formatted): string;8 }

UnixCsvWriter.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 class UnixCsvWriter implements CsvWriter6 {7 public function write(array $line): string8 {9 return join(',', $line) . "\n";

10 }11 }

UnixJsonWriter.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

(continué en la próxima página)

1.1. Creational 5

Page 10: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

5 class UnixJsonWriter implements JsonWriter6 {7 public function write(array $data, bool $formatted): string8 {9 $options = 0;

10

11 if ($formatted) {12 $options = JSON_PRETTY_PRINT;13 }14

15 return json_encode($data, $options);16 }17 }

UnixWriterFactory.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 class UnixWriterFactory implements WriterFactory6 {7 public function createCsvWriter(): CsvWriter8 {9 return new UnixCsvWriter();

10 }11

12 public function createJsonWriter(): JsonWriter13 {14 return new UnixJsonWriter();15 }16 }

WinCsvWriter.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 class WinCsvWriter implements CsvWriter6 {7 public function write(array $line): string8 {9 return join(',', $line) . "\r\n";

10 }11 }

WinJsonWriter.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 class WinJsonWriter implements JsonWriter6 {7 public function write(array $data, bool $formatted): string8 {

(continué en la próxima página)

6 Capítulo 1. Patterns

Page 11: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

9

10

11 return json_encode($data, JSON_PRETTY_PRINT);12 }13 }

WinWriterFactory.php

1 <?php2

3 namespace DesignPatterns\Creational\AbstractFactory;4

5 class WinWriterFactory implements WriterFactory6 {7 public function createCsvWriter(): CsvWriter8 {9 return new WinCsvWriter();

10 }11

12 public function createJsonWriter(): JsonWriter13 {14 return new WinJsonWriter();15 }16 }

Test

Tests/AbstractFactoryTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\AbstractFactory\Tests;4

5 use DesignPatterns\Creational\AbstractFactory\CsvWriter;6 use DesignPatterns\Creational\AbstractFactory\JsonWriter;7 use DesignPatterns\Creational\AbstractFactory\UnixWriterFactory;8 use DesignPatterns\Creational\AbstractFactory\WinWriterFactory;9 use DesignPatterns\Creational\AbstractFactory\WriterFactory;

10 use PHPUnit\Framework\TestCase;11

12 class AbstractFactoryTest extends TestCase13 {14 public function provideFactory()15 {16 return [17 [new UnixWriterFactory()],18 [new WinWriterFactory()]19 ];20 }21

22 /**23 * @dataProvider provideFactory24 *25 * @param WriterFactory $writerFactory26 */

(continué en la próxima página)

1.1. Creational 7

Page 12: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

27 public function testCanCreateCsvWriterOnUnix(WriterFactory $writerFactory)28 {29 $this->assertInstanceOf(JsonWriter::class, $writerFactory->

→˓createJsonWriter());30 $this->assertInstanceOf(CsvWriter::class, $writerFactory->createCsvWriter());31 }32 }

1.1.2 Builder

Purpose

Builder is an interface that build parts of a complex object.

Sometimes, if the builder has a better knowledge of what it builds, this interface could be an abstract class with defaultmethods (aka adapter).

If you have a complex inheritance tree for objects, it is logical to have a complex inheritance tree for builders too.

Note: Builders have often a fluent interface, see the mock builder of PHPUnit for example.

Examples

PHPUnit: Mock Builder

8 Capítulo 1. Patterns

Page 13: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Director.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder;4

5 use DesignPatterns\Creational\Builder\Parts\Vehicle;6

7 /**8 * Director is part of the builder pattern. It knows the interface of the builder9 * and builds a complex object with the help of the builder

10 *11 * You can also inject many builders instead of one to build more complex objects12 */13 class Director14 {15 public function build(Builder $builder): Vehicle16 {

(continué en la próxima página)

1.1. Creational 9

Page 14: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

17 $builder->createVehicle();18 $builder->addDoors();19 $builder->addEngine();20 $builder->addWheel();21

22 return $builder->getVehicle();23 }24 }

Builder.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder;4

5 use DesignPatterns\Creational\Builder\Parts\Vehicle;6

7 interface Builder8 {9 public function createVehicle();

10

11 public function addWheel();12

13 public function addEngine();14

15 public function addDoors();16

17 public function getVehicle(): Vehicle;18 }

TruckBuilder.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder;4

5 use DesignPatterns\Creational\Builder\Parts\Door;6 use DesignPatterns\Creational\Builder\Parts\Engine;7 use DesignPatterns\Creational\Builder\Parts\Wheel;8 use DesignPatterns\Creational\Builder\Parts\Truck;9 use DesignPatterns\Creational\Builder\Parts\Vehicle;

10

11 class TruckBuilder implements Builder12 {13 private Truck $truck;14

15 public function addDoors()16 {17 $this->truck->setPart('rightDoor', new Door());18 $this->truck->setPart('leftDoor', new Door());19 }20

21 public function addEngine()22 {23 $this->truck->setPart('truckEngine', new Engine());24 }25

(continué en la próxima página)

10 Capítulo 1. Patterns

Page 15: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

26 public function addWheel()27 {28 $this->truck->setPart('wheel1', new Wheel());29 $this->truck->setPart('wheel2', new Wheel());30 $this->truck->setPart('wheel3', new Wheel());31 $this->truck->setPart('wheel4', new Wheel());32 $this->truck->setPart('wheel5', new Wheel());33 $this->truck->setPart('wheel6', new Wheel());34 }35

36 public function createVehicle()37 {38 $this->truck = new Truck();39 }40

41 public function getVehicle(): Vehicle42 {43 return $this->truck;44 }45 }

CarBuilder.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder;4

5 use DesignPatterns\Creational\Builder\Parts\Door;6 use DesignPatterns\Creational\Builder\Parts\Engine;7 use DesignPatterns\Creational\Builder\Parts\Wheel;8 use DesignPatterns\Creational\Builder\Parts\Car;9 use DesignPatterns\Creational\Builder\Parts\Vehicle;

10

11 class CarBuilder implements Builder12 {13 private Car $car;14

15 public function addDoors()16 {17 $this->car->setPart('rightDoor', new Door());18 $this->car->setPart('leftDoor', new Door());19 $this->car->setPart('trunkLid', new Door());20 }21

22 public function addEngine()23 {24 $this->car->setPart('engine', new Engine());25 }26

27 public function addWheel()28 {29 $this->car->setPart('wheelLF', new Wheel());30 $this->car->setPart('wheelRF', new Wheel());31 $this->car->setPart('wheelLR', new Wheel());32 $this->car->setPart('wheelRR', new Wheel());33 }34

(continué en la próxima página)

1.1. Creational 11

Page 16: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

35 public function createVehicle()36 {37 $this->car = new Car();38 }39

40 public function getVehicle(): Vehicle41 {42 return $this->car;43 }44 }

Parts/Vehicle.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder\Parts;4

5 abstract class Vehicle6 {7 /**8 * @var object[]9 */

10 private array $data = [];11

12 public function setPart(string $key, object $value)13 {14 $this->data[$key] = $value;15 }16 }

Parts/Truck.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder\Parts;4

5 class Truck extends Vehicle6 {7 }

Parts/Car.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder\Parts;4

5 class Car extends Vehicle6 {7 }

Parts/Engine.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder\Parts;4

(continué en la próxima página)

12 Capítulo 1. Patterns

Page 17: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

5 class Engine6 {7 }

Parts/Wheel.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder\Parts;4

5 class Wheel6 {7 }

Parts/Door.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder\Parts;4

5 class Door6 {7 }

Test

Tests/DirectorTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Builder\Tests;4

5 use DesignPatterns\Creational\Builder\Parts\Car;6 use DesignPatterns\Creational\Builder\Parts\Truck;7 use DesignPatterns\Creational\Builder\TruckBuilder;8 use DesignPatterns\Creational\Builder\CarBuilder;9 use DesignPatterns\Creational\Builder\Director;

10 use PHPUnit\Framework\TestCase;11

12 class DirectorTest extends TestCase13 {14 public function testCanBuildTruck()15 {16 $truckBuilder = new TruckBuilder();17 $newVehicle = (new Director())->build($truckBuilder);18

19 $this->assertInstanceOf(Truck::class, $newVehicle);20 }21

22 public function testCanBuildCar()23 {24 $carBuilder = new CarBuilder();25 $newVehicle = (new Director())->build($carBuilder);26

27 $this->assertInstanceOf(Car::class, $newVehicle);

(continué en la próxima página)

1.1. Creational 13

Page 18: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

28 }29 }

1.1.3 Factory Method

Purpose

The good point over the SimpleFactory is you can subclass it to implement different ways to create objects.

For simple cases, this abstract class could be just an interface.

This pattern is a «real» Design Pattern because it achieves the Dependency Inversion principle a.k.a the «D» in SOLIDprinciples.

It means the FactoryMethod class depends on abstractions, not concrete classes. This is the real trick compared toSimpleFactory or StaticFactory.

14 Capítulo 1. Patterns

Page 19: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Logger.php

1.1. Creational 15

Page 20: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\FactoryMethod;4

5 interface Logger6 {7 public function log(string $message);8 }

StdoutLogger.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\FactoryMethod;4

5 class StdoutLogger implements Logger6 {7 public function log(string $message)8 {9 echo $message;

10 }11 }

FileLogger.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\FactoryMethod;4

5 class FileLogger implements Logger6 {7 private string $filePath;8

9 public function __construct(string $filePath)10 {11 $this->filePath = $filePath;12 }13

14 public function log(string $message)15 {16 file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);17 }18 }

LoggerFactory.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\FactoryMethod;4

5 interface LoggerFactory6 {7 public function createLogger(): Logger;8 }

StdoutLoggerFactory.php

16 Capítulo 1. Patterns

Page 21: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\FactoryMethod;4

5 class StdoutLoggerFactory implements LoggerFactory6 {7 public function createLogger(): Logger8 {9 return new StdoutLogger();

10 }11 }

FileLoggerFactory.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\FactoryMethod;4

5 class FileLoggerFactory implements LoggerFactory6 {7 private string $filePath;8

9 public function __construct(string $filePath)10 {11 $this->filePath = $filePath;12 }13

14 public function createLogger(): Logger15 {16 return new FileLogger($this->filePath);17 }18 }

Test

Tests/FactoryMethodTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\FactoryMethod\Tests;4

5 use DesignPatterns\Creational\FactoryMethod\FileLogger;6 use DesignPatterns\Creational\FactoryMethod\FileLoggerFactory;7 use DesignPatterns\Creational\FactoryMethod\StdoutLogger;8 use DesignPatterns\Creational\FactoryMethod\StdoutLoggerFactory;9 use PHPUnit\Framework\TestCase;

10

11 class FactoryMethodTest extends TestCase12 {13 public function testCanCreateStdoutLogging()14 {15 $loggerFactory = new StdoutLoggerFactory();16 $logger = $loggerFactory->createLogger();17

18 $this->assertInstanceOf(StdoutLogger::class, $logger);19 }

(continué en la próxima página)

1.1. Creational 17

Page 22: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

20

21 public function testCanCreateFileLogging()22 {23 $loggerFactory = new FileLoggerFactory(sys_get_temp_dir());24 $logger = $loggerFactory->createLogger();25

26 $this->assertInstanceOf(FileLogger::class, $logger);27 }28 }

1.1.4 Pool

Purpose

The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use– a «pool» – rather than allocating and destroying them on demand. A client of the pool will request an object fromthe pool and perform operations on the returned object. When the client has finished, it returns the object, which is aspecific type of factory object, to the pool rather than destroying it.

Object pooling can offer a significant performance boost in situations where the cost of initializing a class instance ishigh, the rate of instantiation of a class is high, and the number of instances in use at any one time is low. The pooledobject is obtained in predictable time when creation of the new objects (especially over network) may take variabletime.

However these benefits are mostly true for objects that are expensive with respect to time, such as database connections,socket connections, threads and large graphic objects like fonts or bitmaps. In certain situations, simple object pooling(that hold no external resources, but only occupy memory) may not be efficient and could decrease performance.

18 Capítulo 1. Patterns

Page 23: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

WorkerPool.php

1 <?php declare(strict_types=1);2

(continué en la próxima página)

1.1. Creational 19

Page 24: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

3 namespace DesignPatterns\Creational\Pool;4

5 use Countable;6

7 class WorkerPool implements Countable8 {9 /**

10 * @var StringReverseWorker[]11 */12 private array $occupiedWorkers = [];13

14 /**15 * @var StringReverseWorker[]16 */17 private array $freeWorkers = [];18

19 public function get(): StringReverseWorker20 {21 if (count($this->freeWorkers) == 0) {22 $worker = new StringReverseWorker();23 } else {24 $worker = array_pop($this->freeWorkers);25 }26

27 $this->occupiedWorkers[spl_object_hash($worker)] = $worker;28

29 return $worker;30 }31

32 public function dispose(StringReverseWorker $worker)33 {34 $key = spl_object_hash($worker);35

36 if (isset($this->occupiedWorkers[$key])) {37 unset($this->occupiedWorkers[$key]);38 $this->freeWorkers[$key] = $worker;39 }40 }41

42 public function count(): int43 {44 return count($this->occupiedWorkers) + count($this->freeWorkers);45 }46 }

StringReverseWorker.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Pool;4

5 use DateTime;6

7 class StringReverseWorker8 {9 private DateTime $createdAt;

10

(continué en la próxima página)

20 Capítulo 1. Patterns

Page 25: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

11 public function __construct()12 {13 $this->createdAt = new DateTime();14 }15

16 public function run(string $text)17 {18 return strrev($text);19 }20 }

Test

Tests/PoolTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Pool\Tests;4

5 use DesignPatterns\Creational\Pool\WorkerPool;6 use PHPUnit\Framework\TestCase;7

8 class PoolTest extends TestCase9 {

10 public function testCanGetNewInstancesWithGet()11 {12 $pool = new WorkerPool();13 $worker1 = $pool->get();14 $worker2 = $pool->get();15

16 $this->assertCount(2, $pool);17 $this->assertNotSame($worker1, $worker2);18 }19

20 public function testCanGetSameInstanceTwiceWhenDisposingItFirst()21 {22 $pool = new WorkerPool();23 $worker1 = $pool->get();24 $pool->dispose($worker1);25 $worker2 = $pool->get();26

27 $this->assertCount(1, $pool);28 $this->assertSame($worker1, $worker2);29 }30 }

1.1.5 Prototype

Purpose

To avoid the cost of creating objects the standard way (new Foo()) and instead create a prototype and clone it.

1.1. Creational 21

Page 26: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Examples

Large amounts of data (e.g. create 1,000,000 rows in a database at once via a ORM).

UML Diagram

Code

You can also find this code on GitHub

BookPrototype.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Prototype;4

5 abstract class BookPrototype6 {7 protected string $title;8 protected string $category;9

10 abstract public function __clone();11

12 public function getTitle(): string13 {14 return $this->title;15 }16

(continué en la próxima página)

22 Capítulo 1. Patterns

Page 27: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

17 public function setTitle(string $title)18 {19 $this->title = $title;20 }21 }

BarBookPrototype.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Prototype;4

5 class BarBookPrototype extends BookPrototype6 {7 protected string $category = 'Bar';8

9 public function __clone()10 {11 }12 }

FooBookPrototype.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Prototype;4

5 class FooBookPrototype extends BookPrototype6 {7 protected string $category = 'Foo';8

9 public function __clone()10 {11 }12 }

Test

Tests/PrototypeTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Prototype\Tests;4

5 use DesignPatterns\Creational\Prototype\BarBookPrototype;6 use DesignPatterns\Creational\Prototype\FooBookPrototype;7 use PHPUnit\Framework\TestCase;8

9 class PrototypeTest extends TestCase10 {11 public function testCanGetFooBook()12 {13 $fooPrototype = new FooBookPrototype();14 $barPrototype = new BarBookPrototype();15

(continué en la próxima página)

1.1. Creational 23

Page 28: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

16 for ($i = 0; $i < 10; $i++) {17 $book = clone $fooPrototype;18 $book->setTitle('Foo Book No ' . $i);19 $this->assertInstanceOf(FooBookPrototype::class, $book);20 }21

22 for ($i = 0; $i < 5; $i++) {23 $book = clone $barPrototype;24 $book->setTitle('Bar Book No ' . $i);25 $this->assertInstanceOf(BarBookPrototype::class, $book);26 }27 }28 }

1.1.6 Simple Factory

Purpose

SimpleFactory is a simple factory pattern.

It differs from the static factory because it is not static. Therefore, you can have multiple factories, differently parame-terized, you can subclass it and you can mock it. It always should be preferred over a static factory!

24 Capítulo 1. Patterns

Page 29: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

SimpleFactory.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\SimpleFactory;4

5 class SimpleFactory6 {7 public function createBicycle(): Bicycle8 {9 return new Bicycle();

10 }11 }

Bicycle.php

1.1. Creational 25

Page 30: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\SimpleFactory;4

5 class Bicycle6 {7 public function driveTo(string $destination)8 {9 }

10 }

Usage

1 $factory = new SimpleFactory();2 $bicycle = $factory->createBicycle();3 $bicycle->driveTo('Paris');

Test

Tests/SimpleFactoryTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\SimpleFactory\Tests;4

5 use DesignPatterns\Creational\SimpleFactory\Bicycle;6 use DesignPatterns\Creational\SimpleFactory\SimpleFactory;7 use PHPUnit\Framework\TestCase;8

9 class SimpleFactoryTest extends TestCase10 {11 public function testCanCreateBicycle()12 {13 $bicycle = (new SimpleFactory())->createBicycle();14 $this->assertInstanceOf(Bicycle::class, $bicycle);15 }16 }

1.1.7 Singleton

THIS IS CONSIDERED TO BE AN ANTI-PATTERN! FOR BETTER TESTABILITY AND MAINTAINABI-LITY USE DEPENDENCY INJECTION!

Purpose

To have only one instance of this object in the application that will handle all calls.

Examples

DB Connector

26 Capítulo 1. Patterns

Page 31: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Logger

Lock file for the application (there is only one in the filesystem . . . )

UML Diagram

Code

You can also find this code on GitHub

Singleton.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Singleton;4

5 final class Singleton6 {7 private static ?Singleton $instance = null;8

9 /**10 * gets the instance via lazy initialization (created on first usage)11 */12 public static function getInstance(): Singleton13 {14 if (static::$instance === null) {15 static::$instance = new static();16 }17

18 return static::$instance;19 }20

21 /**22 * is not allowed to call from outside to prevent from creating multiple

→˓instances,23 * to use the singleton, you have to obtain the instance from

→˓Singleton::getInstance() instead24 */25 private function __construct()26 {

(continué en la próxima página)

1.1. Creational 27

Page 32: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

27 }28

29 /**30 * prevent the instance from being cloned (which would create a second instance

→˓of it)31 */32 private function __clone()33 {34 }35

36 /**37 * prevent from being unserialized (which would create a second instance of it)38 */39 private function __wakeup()40 {41 }42 }

Test

Tests/SingletonTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\Singleton\Tests;4

5 use DesignPatterns\Creational\Singleton\Singleton;6 use PHPUnit\Framework\TestCase;7

8 class SingletonTest extends TestCase9 {

10 public function testUniqueness()11 {12 $firstCall = Singleton::getInstance();13 $secondCall = Singleton::getInstance();14

15 $this->assertInstanceOf(Singleton::class, $firstCall);16 $this->assertSame($firstCall, $secondCall);17 }18 }

1.1.8 Static Factory

Purpose

Similar to the AbstractFactory, this pattern is used to create series of related or dependent objects. The differencebetween this and the abstract factory pattern is that the static factory pattern uses just one static method to create alltypes of objects it can create. It is usually named factory or build.

28 Capítulo 1. Patterns

Page 33: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

StaticFactory.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\StaticFactory;4

5 use InvalidArgumentException;6

7 /**8 * Note1: Remember, static means global state which is evil because it can't be

→˓mocked for tests9 * Note2: Cannot be subclassed or mock-upped or have multiple different instances.

10 */11 final class StaticFactory12 {13 public static function factory(string $type): Formatter

(continué en la próxima página)

1.1. Creational 29

Page 34: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

14 {15 if ($type == 'number') {16 return new FormatNumber();17 } elseif ($type == 'string') {18 return new FormatString();19 }20

21 throw new InvalidArgumentException('Unknown format given');22 }23 }

Formatter.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\StaticFactory;4

5 interface Formatter6 {7 public function format(string $input): string;8 }

FormatString.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\StaticFactory;4

5 class FormatString implements Formatter6 {7 public function format(string $input): string8 {9 return $input;

10 }11 }

FormatNumber.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\StaticFactory;4

5 class FormatNumber implements Formatter6 {7 public function format(string $input): string8 {9 return number_format((int) $input);

10 }11 }

Test

Tests/StaticFactoryTest.php

30 Capítulo 1. Patterns

Page 35: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Creational\StaticFactory\Tests;4

5 use InvalidArgumentException;6 use DesignPatterns\Creational\StaticFactory\FormatNumber;7 use DesignPatterns\Creational\StaticFactory\FormatString;8 use DesignPatterns\Creational\StaticFactory\StaticFactory;9 use PHPUnit\Framework\TestCase;

10

11 class StaticFactoryTest extends TestCase12 {13 public function testCanCreateNumberFormatter()14 {15 $this->assertInstanceOf(FormatNumber::class, StaticFactory::factory('number

→˓'));16 }17

18 public function testCanCreateStringFormatter()19 {20 $this->assertInstanceOf(FormatString::class, StaticFactory::factory('string

→˓'));21 }22

23 public function testException()24 {25 $this->expectException(InvalidArgumentException::class);26

27 StaticFactory::factory('object');28 }29 }

1.2 Structural

In Software Engineering, Structural Design Patterns are Design Patterns that ease the design by identifying a simpleway to realize relationships between entities.

1.2.1 Adapter / Wrapper

Purpose

To translate one interface for a class into a compatible interface. An adapter allows classes to work together thatnormally could not because of incompatible interfaces by providing its interface to clients while using the originalinterface.

Examples

DB Client libraries adapter

using multiple different webservices and adapters normalize data so that the outcome is the same for all

1.2. Structural 31

Page 36: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Book.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Adapter;4

5 interface Book6 {7 public function turnPage();8

9 public function open();10

11 public function getPage(): int;12 }

PaperBook.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Adapter;4

(continué en la próxima página)

32 Capítulo 1. Patterns

Page 37: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

5 class PaperBook implements Book6 {7 private int $page;8

9 public function open()10 {11 $this->page = 1;12 }13

14 public function turnPage()15 {16 $this->page++;17 }18

19 public function getPage(): int20 {21 return $this->page;22 }23 }

EBook.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Adapter;4

5 interface EBook6 {7 public function unlock();8

9 public function pressNext();10

11 /**12 * returns current page and total number of pages, like [10, 100] is page 10 of

→˓10013 *14 * @return int[]15 */16 public function getPage(): array;17 }

EBookAdapter.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Adapter;4

5 /**6 * This is the adapter here. Notice it implements Book,7 * therefore you don't have to change the code of the client which is using a Book8 */9 class EBookAdapter implements Book

10 {11 protected EBook $eBook;12

13 public function __construct(EBook $eBook)14 {

(continué en la próxima página)

1.2. Structural 33

Page 38: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

15 $this->eBook = $eBook;16 }17

18 /**19 * This class makes the proper translation from one interface to another.20 */21 public function open()22 {23 $this->eBook->unlock();24 }25

26 public function turnPage()27 {28 $this->eBook->pressNext();29 }30

31 /**32 * notice the adapted behavior here: EBook::getPage() will return two integers,

→˓but Book33 * supports only a current page getter, so we adapt the behavior here34 */35 public function getPage(): int36 {37 return $this->eBook->getPage()[0];38 }39 }

Kindle.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Adapter;4

5 /**6 * this is the adapted class. In production code, this could be a class from another

→˓package, some vendor code.7 * Notice that it uses another naming scheme and the implementation does something

→˓similar but in another way8 */9 class Kindle implements EBook

10 {11 private int $page = 1;12 private int $totalPages = 100;13

14 public function pressNext()15 {16 $this->page++;17 }18

19 public function unlock()20 {21 }22

23 /**24 * returns current page and total number of pages, like [10, 100] is page 10 of

→˓10025 *

(continué en la próxima página)

34 Capítulo 1. Patterns

Page 39: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

26 * @return int[]27 */28 public function getPage(): array29 {30 return [$this->page, $this->totalPages];31 }32 }

Test

Tests/AdapterTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Adapter\Tests;4

5 use DesignPatterns\Structural\Adapter\PaperBook;6 use DesignPatterns\Structural\Adapter\EBookAdapter;7 use DesignPatterns\Structural\Adapter\Kindle;8 use PHPUnit\Framework\TestCase;9

10 class AdapterTest extends TestCase11 {12 public function testCanTurnPageOnBook()13 {14 $book = new PaperBook();15 $book->open();16 $book->turnPage();17

18 $this->assertSame(2, $book->getPage());19 }20

21 public function testCanTurnPageOnKindleLikeInANormalBook()22 {23 $kindle = new Kindle();24 $book = new EBookAdapter($kindle);25

26 $book->open();27 $book->turnPage();28

29 $this->assertSame(2, $book->getPage());30 }31 }

1.2.2 Bridge

Purpose

Decouple an abstraction from its implementation so that the two can vary independently.

1.2. Structural 35

Page 40: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

36 Capítulo 1. Patterns

Page 41: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

Formatter.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Bridge;4

5 interface Formatter6 {7 public function format(string $text): string;8 }

PlainTextFormatter.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Bridge;4

5 class PlainTextFormatter implements Formatter6 {7 public function format(string $text): string8 {9 return $text;

10 }11 }

HtmlFormatter.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Bridge;4

5 class HtmlFormatter implements Formatter6 {7 public function format(string $text): string8 {9 return sprintf('<p>%s</p>', $text);

10 }11 }

Service.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Bridge;4

5 abstract class Service6 {7 protected Formatter $implementation;8

9 public function __construct(Formatter $printer)10 {11 $this->implementation = $printer;12 }13

(continué en la próxima página)

1.2. Structural 37

Page 42: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

14 public function setImplementation(Formatter $printer)15 {16 $this->implementation = $printer;17 }18

19 abstract public function get(): string;20 }

HelloWorldService.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Bridge;4

5 class HelloWorldService extends Service6 {7 public function get(): string8 {9 return $this->implementation->format('Hello World');

10 }11 }

PingService.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Bridge;4

5 class PingService extends Service6 {7 public function get(): string8 {9 return $this->implementation->format('pong');

10 }11 }

Test

Tests/BridgeTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Bridge\Tests;4

5 use DesignPatterns\Structural\Bridge\HelloWorldService;6 use DesignPatterns\Structural\Bridge\HtmlFormatter;7 use DesignPatterns\Structural\Bridge\PlainTextFormatter;8 use PHPUnit\Framework\TestCase;9

10 class BridgeTest extends TestCase11 {12 public function testCanPrintUsingThePlainTextFormatter()13 {14 $service = new HelloWorldService(new PlainTextFormatter());15

(continué en la próxima página)

38 Capítulo 1. Patterns

Page 43: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

16 $this->assertSame('Hello World', $service->get());17 }18

19 public function testCanPrintUsingTheHtmlFormatter()20 {21 $service = new HelloWorldService(new HtmlFormatter());22

23 $this->assertSame('<p>Hello World</p>', $service->get());24 }25 }

1.2.3 Composite

Purpose

To treat a group of objects the same way as a single instance of the object.

Examples

a form class instance handles all its form elements like a single instance of the form, when render() is called,it subsequently runs through all its child elements and calls render() on them

UML Diagram

Code

You can also find this code on GitHub

Renderable.php

1.2. Structural 39

Page 44: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Composite;4

5 interface Renderable6 {7 public function render(): string;8 }

Form.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Composite;4

5 /**6 * The composite node MUST extend the component contract. This is mandatory for

→˓building7 * a tree of components.8 */9 class Form implements Renderable

10 {11 /**12 * @var Renderable[]13 */14 private array $elements;15

16 /**17 * runs through all elements and calls render() on them, then returns the

→˓complete representation18 * of the form.19 *20 * from the outside, one will not see this and the form will act like a single

→˓object instance21 */22 public function render(): string23 {24 $formCode = '<form>';25

26 foreach ($this->elements as $element) {27 $formCode .= $element->render();28 }29

30 $formCode .= '</form>';31

32 return $formCode;33 }34

35 public function addElement(Renderable $element)36 {37 $this->elements[] = $element;38 }39 }

InputElement.php

1 <?php declare(strict_types=1);(continué en la próxima página)

40 Capítulo 1. Patterns

Page 45: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

2

3 namespace DesignPatterns\Structural\Composite;4

5 class InputElement implements Renderable6 {7 public function render(): string8 {9 return '<input type="text" />';

10 }11 }

TextElement.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Composite;4

5 class TextElement implements Renderable6 {7 private string $text;8

9 public function __construct(string $text)10 {11 $this->text = $text;12 }13

14 public function render(): string15 {16 return $this->text;17 }18 }

Test

Tests/CompositeTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Composite\Tests;4

5 use DesignPatterns\Structural\Composite\Form;6 use DesignPatterns\Structural\Composite\TextElement;7 use DesignPatterns\Structural\Composite\InputElement;8 use PHPUnit\Framework\TestCase;9

10 class CompositeTest extends TestCase11 {12 public function testRender()13 {14 $form = new Form();15 $form->addElement(new TextElement('Email:'));16 $form->addElement(new InputElement());17 $embed = new Form();18 $embed->addElement(new TextElement('Password:'));19 $embed->addElement(new InputElement());

(continué en la próxima página)

1.2. Structural 41

Page 46: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

20 $form->addElement($embed);21

22 // This is just an example, in a real world scenario it is important to→˓remember that web browsers do not

23 // currently support nested forms24

25 $this->assertSame(26 '<form>Email:<input type="text" /><form>Password:<input type="text" /></

→˓form></form>',27 $form->render()28 );29 }30 }

1.2.4 Data Mapper

Purpose

A Data Mapper, is a Data Access Layer that performs bidirectional transfer of data between a persistent data store(often a relational database) and an in memory data representation (the domain layer). The goal of the pattern isto keep the in memory representation and the persistent data store independent of each other and the data mapperitself. The layer is composed of one or more mappers (or Data Access Objects), performing the data transfer. Mapperimplementations vary in scope. Generic mappers will handle many different domain entity types, dedicated mapperswill handle one or a few.

The key point of this pattern is, unlike Active Record pattern, the data model follows Single Responsibility Principle.

Examples

DB Object Relational Mapper (ORM) : Doctrine2 uses DAO named as «EntityRepository»

42 Capítulo 1. Patterns

Page 47: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

User.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\DataMapper;4

5 class User6 {7 private string $username;8 private string $email;9

10 public static function fromState(array $state): User11 {

(continué en la próxima página)

1.2. Structural 43

Page 48: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 // validate state before accessing keys!13

14 return new self(15 $state['username'],16 $state['email']17 );18 }19

20 public function __construct(string $username, string $email)21 {22 // validate parameters before setting them!23

24 $this->username = $username;25 $this->email = $email;26 }27

28 public function getUsername(): string29 {30 return $this->username;31 }32

33 public function getEmail(): string34 {35 return $this->email;36 }37 }

UserMapper.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\DataMapper;4

5 use InvalidArgumentException;6

7 class UserMapper8 {9 private StorageAdapter $adapter;

10

11 public function __construct(StorageAdapter $storage)12 {13 $this->adapter = $storage;14 }15

16 /**17 * finds a user from storage based on ID and returns a User object located18 * in memory. Normally this kind of logic will be implemented using the

→˓Repository pattern.19 * However the important part is in mapRowToUser() below, that will create a

→˓business object from the20 * data fetched from storage21 */22 public function findById(int $id): User23 {24 $result = $this->adapter->find($id);25

26 if ($result === null) {(continué en la próxima página)

44 Capítulo 1. Patterns

Page 49: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

27 throw new InvalidArgumentException("User #$id not found");28 }29

30 return $this->mapRowToUser($result);31 }32

33 private function mapRowToUser(array $row): User34 {35 return User::fromState($row);36 }37 }

StorageAdapter.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\DataMapper;4

5 class StorageAdapter6 {7 private array $data = [];8

9 public function __construct(array $data)10 {11 $this->data = $data;12 }13

14 /**15 * @param int $id16 *17 * @return array|null18 */19 public function find(int $id)20 {21 if (isset($this->data[$id])) {22 return $this->data[$id];23 }24

25 return null;26 }27 }

Test

Tests/DataMapperTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\DataMapper\Tests;4

5 use InvalidArgumentException;6 use DesignPatterns\Structural\DataMapper\StorageAdapter;7 use DesignPatterns\Structural\DataMapper\User;8 use DesignPatterns\Structural\DataMapper\UserMapper;9 use PHPUnit\Framework\TestCase;

(continué en la próxima página)

1.2. Structural 45

Page 50: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

10

11 class DataMapperTest extends TestCase12 {13 public function testCanMapUserFromStorage()14 {15 $storage = new StorageAdapter([1 => ['username' => 'domnikl', 'email' =>

→˓'[email protected]']]);16 $mapper = new UserMapper($storage);17

18 $user = $mapper->findById(1);19

20 $this->assertInstanceOf(User::class, $user);21 }22

23 public function testWillNotMapInvalidData()24 {25 $this->expectException(InvalidArgumentException::class);26

27 $storage = new StorageAdapter([]);28 $mapper = new UserMapper($storage);29

30 $mapper->findById(1);31 }32 }

1.2.5 Decorator

Purpose

To dynamically add new functionality to class instances.

Examples

Web Service Layer: Decorators JSON and XML for a REST service (in this case, only one of these should beallowed of course)

46 Capítulo 1. Patterns

Page 51: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Booking.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Decorator;4

5 interface Booking6 {7 public function calculatePrice(): int;8

9 public function getDescription(): string;10 }

BookingDecorator.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Decorator;

(continué en la próxima página)

1.2. Structural 47

Page 52: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

4

5 abstract class BookingDecorator implements Booking6 {7 protected Booking $booking;8

9 public function __construct(Booking $booking)10 {11 $this->booking = $booking;12 }13 }

DoubleRoomBooking.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Decorator;4

5 class DoubleRoomBooking implements Booking6 {7 public function calculatePrice(): int8 {9 return 40;

10 }11

12 public function getDescription(): string13 {14 return 'double room';15 }16 }

ExtraBed.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Decorator;4

5 class ExtraBed extends BookingDecorator6 {7 private const PRICE = 30;8

9 public function calculatePrice(): int10 {11 return $this->booking->calculatePrice() + self::PRICE;12 }13

14 public function getDescription(): string15 {16 return $this->booking->getDescription() . ' with extra bed';17 }18 }

WiFi.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Decorator;4

(continué en la próxima página)

48 Capítulo 1. Patterns

Page 53: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

5 class WiFi extends BookingDecorator6 {7 private const PRICE = 2;8

9 public function calculatePrice(): int10 {11 return $this->booking->calculatePrice() + self::PRICE;12 }13

14 public function getDescription(): string15 {16 return $this->booking->getDescription() . ' with wifi';17 }18 }

Test

Tests/DecoratorTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Decorator\Tests;4

5 use DesignPatterns\Structural\Decorator\DoubleRoomBooking;6 use DesignPatterns\Structural\Decorator\ExtraBed;7 use DesignPatterns\Structural\Decorator\WiFi;8 use PHPUnit\Framework\TestCase;9

10 class DecoratorTest extends TestCase11 {12 public function testCanCalculatePriceForBasicDoubleRoomBooking()13 {14 $booking = new DoubleRoomBooking();15

16 $this->assertSame(40, $booking->calculatePrice());17 $this->assertSame('double room', $booking->getDescription());18 }19

20 public function testCanCalculatePriceForDoubleRoomBookingWithWiFi()21 {22 $booking = new DoubleRoomBooking();23 $booking = new WiFi($booking);24

25 $this->assertSame(42, $booking->calculatePrice());26 $this->assertSame('double room with wifi', $booking->getDescription());27 }28

29 public function testCanCalculatePriceForDoubleRoomBookingWithWiFiAndExtraBed()30 {31 $booking = new DoubleRoomBooking();32 $booking = new WiFi($booking);33 $booking = new ExtraBed($booking);34

35 $this->assertSame(72, $booking->calculatePrice());36 $this->assertSame('double room with wifi with extra bed', $booking->

→˓getDescription()); (continué en la próxima página)

1.2. Structural 49

Page 54: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

37 }38 }

1.2.6 Dependency Injection

Purpose

To implement a loosely coupled architecture in order to get better testable, maintainable and extendable code.

Usage

DatabaseConfiguration gets injected and DatabaseConnection will get all that it needs from $config.Without DI, the configuration would be created directly in DatabaseConnection, which is not very good fortesting and extending it.

Examples

The Doctrine2 ORM uses dependency injection e.g. for configuration that is injected into a Connectionobject. For testing purposes, one can easily create a mock object of the configuration and inject that into theConnection object

many frameworks already have containers for DI that create objects via a configuration array and inject themwhere needed (i.e. in Controllers)

50 Capítulo 1. Patterns

Page 55: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

DatabaseConfiguration.php

1.2. Structural 51

Page 56: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\DependencyInjection;4

5 class DatabaseConfiguration6 {7 private string $host;8 private int $port;9 private string $username;

10 private string $password;11

12 public function __construct(string $host, int $port, string $username, string→˓$password)

13 {14 $this->host = $host;15 $this->port = $port;16 $this->username = $username;17 $this->password = $password;18 }19

20 public function getHost(): string21 {22 return $this->host;23 }24

25 public function getPort(): int26 {27 return $this->port;28 }29

30 public function getUsername(): string31 {32 return $this->username;33 }34

35 public function getPassword(): string36 {37 return $this->password;38 }39 }

DatabaseConnection.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\DependencyInjection;4

5 class DatabaseConnection6 {7 private DatabaseConfiguration $configuration;8

9 public function __construct(DatabaseConfiguration $config)10 {11 $this->configuration = $config;12 }13

14 public function getDsn(): string

(continué en la próxima página)

52 Capítulo 1. Patterns

Page 57: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

15 {16 // this is just for the sake of demonstration, not a real DSN17 // notice that only the injected config is used here, so there is18 // a real separation of concerns here19

20 return sprintf(21 '%s:%s@%s:%d',22 $this->configuration->getUsername(),23 $this->configuration->getPassword(),24 $this->configuration->getHost(),25 $this->configuration->getPort()26 );27 }28 }

Test

Tests/DependencyInjectionTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\DependencyInjection\Tests;4

5 use DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration;6 use DesignPatterns\Structural\DependencyInjection\DatabaseConnection;7 use PHPUnit\Framework\TestCase;8

9 class DependencyInjectionTest extends TestCase10 {11 public function testDependencyInjection()12 {13 $config = new DatabaseConfiguration('localhost', 3306, 'domnikl', '1234');14 $connection = new DatabaseConnection($config);15

16 $this->assertSame('domnikl:1234@localhost:3306', $connection->getDsn());17 }18 }

1.2.7 Facade

Purpose

The primary goal of a Facade Pattern is not to avoid you having to read the manual of a complex API. It’s only aside-effect. The first goal is to reduce coupling and follow the Law of Demeter.

A Facade is meant to decouple a client and a sub-system by embedding many (but sometimes just one) interface, andof course to reduce complexity.

A facade does not forbid you the access to the sub-system

You can (you should) have multiple facades for one sub-system

That’s why a good facade has no new in it. If there are multiple creations for each method, it is not a Facade, it’s aBuilder or a [Abstract|Static|Simple] Factory [Method].

1.2. Structural 53

Page 58: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

The best facade has no new and a constructor with interface-type-hinted parameters. If you need creation of newinstances, use a Factory as argument.

UML Diagram

Code

You can also find this code on GitHub

Facade.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Facade;4

5 class Facade6 {

(continué en la próxima página)

54 Capítulo 1. Patterns

Page 59: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

7 private OperatingSystem $os;8 private Bios $bios;9

10 public function __construct(Bios $bios, OperatingSystem $os)11 {12 $this->bios = $bios;13 $this->os = $os;14 }15

16 public function turnOn()17 {18 $this->bios->execute();19 $this->bios->waitForKeyPress();20 $this->bios->launch($this->os);21 }22

23 public function turnOff()24 {25 $this->os->halt();26 $this->bios->powerDown();27 }28 }

OperatingSystem.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Facade;4

5 interface OperatingSystem6 {7 public function halt();8

9 public function getName(): string;10 }

Bios.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Facade;4

5 interface Bios6 {7 public function execute();8

9 public function waitForKeyPress();10

11 public function launch(OperatingSystem $os);12

13 public function powerDown();14 }

Test

Tests/FacadeTest.php

1.2. Structural 55

Page 60: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Facade\Tests;4

5 use DesignPatterns\Structural\Facade\Bios;6 use DesignPatterns\Structural\Facade\Facade;7 use DesignPatterns\Structural\Facade\OperatingSystem;8 use PHPUnit\Framework\TestCase;9

10 class FacadeTest extends TestCase11 {12 public function testComputerOn()13 {14 $os = $this->createMock(OperatingSystem::class);15

16 $os->method('getName')17 ->will($this->returnValue('Linux'));18

19 $bios = $this->createMock(Bios::class);20

21 $bios->method('launch')22 ->with($os);23

24 /** @noinspection PhpParamsInspection */25 $facade = new Facade($bios, $os);26 $facade->turnOn();27

28 $this->assertSame('Linux', $os->getName());29 }30 }

1.2.8 Fluent Interface

Purpose

To write code that is easy readable just like sentences in a natural language (like English).

Examples

Doctrine2’s QueryBuilder works something like that example class below

PHPUnit uses fluent interfaces to build mock objects

56 Capítulo 1. Patterns

Page 61: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Sql.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\FluentInterface;4

5 class Sql6 {7 private array $fields = [];8 private array $from = [];9 private array $where = [];

10

11 public function select(array $fields): Sql

(continué en la próxima página)

1.2. Structural 57

Page 62: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 {13 $this->fields = $fields;14

15 return $this;16 }17

18 public function from(string $table, string $alias): Sql19 {20 $this->from[] = $table.' AS '.$alias;21

22 return $this;23 }24

25 public function where(string $condition): Sql26 {27 $this->where[] = $condition;28

29 return $this;30 }31

32 public function __toString(): string33 {34 return sprintf(35 'SELECT %s FROM %s WHERE %s',36 join(', ', $this->fields),37 join(', ', $this->from),38 join(' AND ', $this->where)39 );40 }41 }

Test

Tests/FluentInterfaceTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\FluentInterface\Tests;4

5 use DesignPatterns\Structural\FluentInterface\Sql;6 use PHPUnit\Framework\TestCase;7

8 class FluentInterfaceTest extends TestCase9 {

10 public function testBuildSQL()11 {12 $query = (new Sql())13 ->select(['foo', 'bar'])14 ->from('foobar', 'f')15 ->where('f.bar = ?');16

17 $this->assertSame('SELECT foo, bar FROM foobar AS f WHERE f.bar = ?',→˓(string) $query);

18 }19 }

58 Capítulo 1. Patterns

Page 63: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1.2.9 Flyweight

Purpose

To minimise memory usage, a Flyweight shares as much as possible memory with similar objects. It is needed whena large amount of objects is used that don’t differ much in state. A common practice is to hold state in external datastructures and pass them to the flyweight object when needed.

UML Diagram

1.2. Structural 59

Page 64: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

Text.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Flyweight;4

5 /**6 * This is the interface that all flyweights need to implement7 */8 interface Text9 {

10 public function render(string $extrinsicState): string;11 }

Word.php

1 <?php2

3 namespace DesignPatterns\Structural\Flyweight;4

5 class Word implements Text6 {7 private string $name;8

9 public function __construct(string $name)10 {11 $this->name = $name;12 }13

14 public function render(string $font): string15 {16 return sprintf('Word %s with font %s', $this->name, $font);17 }18 }

Character.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Flyweight;4

5 /**6 * Implements the flyweight interface and adds storage for intrinsic state, if any.7 * Instances of concrete flyweights are shared by means of a factory.8 */9 class Character implements Text

10 {11 /**12 * Any state stored by the concrete flyweight must be independent of its context.13 * For flyweights representing characters, this is usually the corresponding

→˓character code.14 */15 private string $name;16

(continué en la próxima página)

60 Capítulo 1. Patterns

Page 65: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

17 public function __construct(string $name)18 {19 $this->name = $name;20 }21

22 public function render(string $font): string23 {24 // Clients supply the context-dependent information that the flyweight needs

→˓to draw itself25 // For flyweights representing characters, extrinsic state usually contains

→˓e.g. the font.26

27 return sprintf('Character %s with font %s', $this->name, $font);28 }29 }

TextFactory.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Flyweight;4

5 use Countable;6

7 /**8 * A factory manages shared flyweights. Clients should not instantiate them directly,9 * but let the factory take care of returning existing objects or creating new ones.

10 */11 class TextFactory implements Countable12 {13 /**14 * @var Text[]15 */16 private array $charPool = [];17

18 public function get(string $name): Text19 {20 if (!isset($this->charPool[$name])) {21 $this->charPool[$name] = $this->create($name);22 }23

24 return $this->charPool[$name];25 }26

27 private function create(string $name): Text28 {29 if (strlen($name) == 1) {30 return new Character($name);31 } else {32 return new Word($name);33 }34 }35

36 public function count(): int37 {38 return count($this->charPool);39 }

(continué en la próxima página)

1.2. Structural 61

Page 66: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

40 }

Test

Tests/FlyweightTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Flyweight\Tests;4

5 use DesignPatterns\Structural\Flyweight\TextFactory;6 use PHPUnit\Framework\TestCase;7

8 class FlyweightTest extends TestCase9 {

10 private array $characters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k→˓',

11 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];12

13 private array $fonts = ['Arial', 'Times New Roman', 'Verdana', 'Helvetica'];14

15 public function testFlyweight()16 {17 $factory = new TextFactory();18

19 for ($i = 0; $i <= 10; $i++) {20 foreach ($this->characters as $char) {21 foreach ($this->fonts as $font) {22 $flyweight = $factory->get($char);23 $rendered = $flyweight->render($font);24

25 $this->assertSame(sprintf('Character %s with font %s', $char,→˓$font), $rendered);

26 }27 }28 }29

30 foreach ($this->fonts as $word) {31 $flyweight = $factory->get($word);32 $rendered = $flyweight->render('foobar');33

34 $this->assertSame(sprintf('Word %s with font foobar', $word), $rendered);35 }36

37 // Flyweight pattern ensures that instances are shared38 // instead of having hundreds of thousands of individual objects39 // there must be one instance for every char that has been reused for

→˓displaying in different fonts40 $this->assertCount(count($this->characters) + count($this->fonts), $factory);41 }42 }

1.2.10 Proxy

62 Capítulo 1. Patterns

Page 67: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Purpose

To interface to anything that is expensive or impossible to duplicate.

Examples

Doctrine2 uses proxies to implement framework magic (e.g. lazy initialization) in them, while the user stillworks with his own entity classes and will never use nor touch the proxies

1.2. Structural 63

Page 68: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

64 Capítulo 1. Patterns

Page 69: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

BankAccount.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Proxy;4

5 interface BankAccount6 {7 public function deposit(int $amount);8

9 public function getBalance(): int;10 }

HeavyBankAccount.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Proxy;4

5 class HeavyBankAccount implements BankAccount6 {7 /**8 * @var int[]9 */

10 private array $transactions = [];11

12 public function deposit(int $amount)13 {14 $this->transactions[] = $amount;15 }16

17 public function getBalance(): int18 {19 // this is the heavy part, imagine all the transactions even from20 // years and decades ago must be fetched from a database or web service21 // and the balance must be calculated from it22

23 return (int) array_sum($this->transactions);24 }25 }

BankAccountProxy.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Proxy;4

5 class BankAccountProxy extends HeavyBankAccount implements BankAccount6 {7 private ?int $balance = null;8

9 public function getBalance(): int10 {11 // because calculating balance is so expensive,

(continué en la próxima página)

1.2. Structural 65

Page 70: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 // the usage of BankAccount::getBalance() is delayed until it really is needed13 // and will not be calculated again for this instance14

15 if ($this->balance === null) {16 $this->balance = parent::getBalance();17 }18

19 return $this->balance;20 }21 }

Test

ProxyTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Proxy\Tests;4

5 use DesignPatterns\Structural\Proxy\BankAccountProxy;6 use PHPUnit\Framework\TestCase;7

8 class ProxyTest extends TestCase9 {

10 public function testProxyWillOnlyExecuteExpensiveGetBalanceOnce()11 {12 $bankAccount = new BankAccountProxy();13 $bankAccount->deposit(30);14

15 // this time balance is being calculated16 $this->assertSame(30, $bankAccount->getBalance());17

18 // inheritance allows for BankAccountProxy to behave to an outsider exactly→˓like ServerBankAccount

19 $bankAccount->deposit(50);20

21 // this time the previously calculated balance is returned again without re-→˓calculating it

22 $this->assertSame(30, $bankAccount->getBalance());23 }24 }

1.2.11 Registry

Purpose

To implement a central storage for objects often used throughout the application, is typically implemented using anabstract class with only static methods (or using the Singleton pattern). Remember that this introduces global state,which should be avoided at all times! Instead implement it using Dependency Injection!

66 Capítulo 1. Patterns

Page 71: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Registry.php

1 <?php declare(strict_types=1);2

(continué en la próxima página)

1.2. Structural 67

Page 72: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

3 namespace DesignPatterns\Structural\Registry;4

5 use InvalidArgumentException;6

7 abstract class Registry8 {9 const LOGGER = 'logger';

10

11 /**12 * this introduces global state in your application which can not be mocked up

→˓for testing13 * and is therefor considered an anti-pattern! Use dependency injection instead!14 *15 * @var Service[]16 */17 private static array $services = [];18

19 private static array $allowedKeys = [20 self::LOGGER,21 ];22

23 public static function set(string $key, Service $value)24 {25 if (!in_array($key, self::$allowedKeys)) {26 throw new InvalidArgumentException('Invalid key given');27 }28

29 self::$services[$key] = $value;30 }31

32 public static function get(string $key): Service33 {34 if (!in_array($key, self::$allowedKeys) || !isset(self::$services[$key])) {35 throw new InvalidArgumentException('Invalid key given');36 }37

38 return self::$services[$key];39 }40 }

Service.php

1 <?php2

3 namespace DesignPatterns\Structural\Registry;4

5 class Service6 {7

8 }

Test

Tests/RegistryTest.php

68 Capítulo 1. Patterns

Page 73: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Structural\Registry\Tests;4

5 use InvalidArgumentException;6 use DesignPatterns\Structural\Registry\Registry;7 use DesignPatterns\Structural\Registry\Service;8 use PHPUnit\Framework\MockObject\MockObject;9 use PHPUnit\Framework\TestCase;

10

11 class RegistryTest extends TestCase12 {13 /**14 * @var Service15 */16 private MockObject $service;17

18 protected function setUp(): void19 {20 $this->service = $this->getMockBuilder(Service::class)->getMock();21 }22

23 public function testSetAndGetLogger()24 {25 Registry::set(Registry::LOGGER, $this->service);26

27 $this->assertSame($this->service, Registry::get(Registry::LOGGER));28 }29

30 public function testThrowsExceptionWhenTryingToSetInvalidKey()31 {32 $this->expectException(InvalidArgumentException::class);33

34 Registry::set('foobar', $this->service);35 }36

37 /**38 * notice @runInSeparateProcess here: without it, a previous test might have set

→˓it already and39 * testing would not be possible. That's why you should implement Dependency

→˓Injection where an40 * injected class may easily be replaced by a mockup41 *42 * @runInSeparateProcess43 */44 public function testThrowsExceptionWhenTryingToGetNotSetKey()45 {46 $this->expectException(InvalidArgumentException::class);47

48 Registry::get(Registry::LOGGER);49 }50 }

1.2. Structural 69

Page 74: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1.3 Behavioral

In software engineering, behavioral design patterns are design patterns that identify common communication pat-terns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out thiscommunication.

1.3.1 Chain Of Responsibilities

Purpose

To build a chain of objects to handle a call in sequential order. If one object cannot handle a call, it delegates the callto the next in the chain and so forth.

Examples

logging framework, where each chain element decides autonomously what to do with a log message

a Spam filter

Caching: first object is an instance of e.g. a Memcached Interface, if that «misses» it delegates the call to thedatabase interface

UML Diagram

70 Capítulo 1. Patterns

Page 75: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

Handler.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\ChainOfResponsibilities;4

5 use Psr\Http\Message\RequestInterface;6

7 abstract class Handler8 {9 private ?Handler $successor = null;

10

11 public function __construct(Handler $handler = null)12 {13 $this->successor = $handler;14 }15

16 /**17 * This approach by using a template method pattern ensures you that18 * each subclass will not forget to call the successor19 */20 final public function handle(RequestInterface $request): ?string21 {22 $processed = $this->processing($request);23

24 if ($processed === null && $this->successor !== null) {25 // the request has not been processed by this handler => see the next26 $processed = $this->successor->handle($request);27 }28

29 return $processed;30 }31

32 abstract protected function processing(RequestInterface $request): ?string;33 }

Responsible/FastStorage.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;4

5 use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;6 use Psr\Http\Message\RequestInterface;7

8 class HttpInMemoryCacheHandler extends Handler9 {

10 private array $data;11

12 public function __construct(array $data, ?Handler $successor = null)13 {14 parent::__construct($successor);15

16 $this->data = $data;

(continué en la próxima página)

1.3. Behavioral 71

Page 76: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

17 }18

19 protected function processing(RequestInterface $request): ?string20 {21 $key = sprintf(22 '%s?%s',23 $request->getUri()->getPath(),24 $request->getUri()->getQuery()25 );26

27 if ($request->getMethod() == 'GET' && isset($this->data[$key])) {28 return $this->data[$key];29 }30

31 return null;32 }33 }

Responsible/SlowStorage.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;4

5 use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;6 use Psr\Http\Message\RequestInterface;7

8 class SlowDatabaseHandler extends Handler9 {

10 protected function processing(RequestInterface $request): ?string11 {12 // this is a mockup, in production code you would ask a slow (compared to in-

→˓memory) DB for the results13

14 return 'Hello World!';15 }16 }

Test

Tests/ChainTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Tests;4

5 use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;6 use

→˓DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\HttpInMemoryCacheHandler;→˓

7 use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowDatabaseHandler;8 use PHPUnit\Framework\TestCase;9 use Psr\Http\Message\RequestInterface;

10 use Psr\Http\Message\UriInterface;11

(continué en la próxima página)

72 Capítulo 1. Patterns

Page 77: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 class ChainTest extends TestCase13 {14 private Handler $chain;15

16 protected function setUp(): void17 {18 $this->chain = new HttpInMemoryCacheHandler(19 ['/foo/bar?index=1' => 'Hello In Memory!'],20 new SlowDatabaseHandler()21 );22 }23

24 public function testCanRequestKeyInFastStorage()25 {26 $uri = $this->createMock(UriInterface::class);27 $uri->method('getPath')->willReturn('/foo/bar');28 $uri->method('getQuery')->willReturn('index=1');29

30 $request = $this->createMock(RequestInterface::class);31 $request->method('getMethod')32 ->willReturn('GET');33 $request->method('getUri')->willReturn($uri);34

35 $this->assertSame('Hello In Memory!', $this->chain->handle($request));36 }37

38 public function testCanRequestKeyInSlowStorage()39 {40 $uri = $this->createMock(UriInterface::class);41 $uri->method('getPath')->willReturn('/foo/baz');42 $uri->method('getQuery')->willReturn('');43

44 $request = $this->createMock(RequestInterface::class);45 $request->method('getMethod')46 ->willReturn('GET');47 $request->method('getUri')->willReturn($uri);48

49 $this->assertSame('Hello World!', $this->chain->handle($request));50 }51 }

1.3.2 Command

Purpose

To encapsulate invocation and decoupling.

We have an Invoker and a Receiver. This pattern uses a «Command» to delegate the method call against the Receiverand presents the same method «execute». Therefore, the Invoker just knows to call «execute» to process the Commandof the client. The Receiver is decoupled from the Invoker.

The second aspect of this pattern is the undo(), which undoes the method execute(). Command can also be aggregatedto combine more complex commands with minimum copy-paste and relying on composition over inheritance.

1.3. Behavioral 73

Page 78: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Examples

A text editor : all events are commands which can be undone, stacked and saved.

big CLI tools use subcommands to distribute various tasks and pack them in «modules», each of these can beimplemented with the Command pattern (e.g. vagrant)

UML Diagram

Code

You can also find this code on GitHub

Command.php

74 Capítulo 1. Patterns

Page 79: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command;4

5 interface Command6 {7 /**8 * this is the most important method in the Command pattern,9 * The Receiver goes in the constructor.

10 */11 public function execute();12 }

UndoableCommand.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command;4

5 interface UndoableCommand extends Command6 {7 /**8 * This method is used to undo change made by command execution9 */

10 public function undo();11 }

HelloCommand.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command;4

5 /**6 * This concrete command calls "print" on the Receiver, but an external7 * invoker just knows that it can call "execute"8 */9 class HelloCommand implements Command

10 {11 private Receiver $output;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 parameters16 */17 public function __construct(Receiver $console)18 {19 $this->output = $console;20 }21

22 /**23 * execute and output "Hello World".24 */25 public function execute()26 {27 // sometimes, there is no receiver and this is the command which does all the

→˓work(continué en la próxima página)

1.3. Behavioral 75

Page 80: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

28 $this->output->write('Hello World');29 }30 }

AddMessageDateCommand.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command;4

5 /**6 * This concrete command tweaks receiver to add current date to messages7 * invoker just knows that it can call "execute"8 */9 class AddMessageDateCommand implements UndoableCommand

10 {11 private Receiver $output;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(Receiver $console)18 {19 $this->output = $console;20 }21

22 /**23 * Execute and make receiver to enable displaying messages date.24 */25 public function execute()26 {27 // sometimes, there is no receiver and this is the command which28 // does all the work29 $this->output->enableDate();30 }31

32 /**33 * Undo the command and make receiver to disable displaying messages date.34 */35 public function undo()36 {37 // sometimes, there is no receiver and this is the command which38 // does all the work39 $this->output->disableDate();40 }41 }

Receiver.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command;4

5 /**6 * Receiver is a specific service with its own contract and can be only concrete.

(continué en la próxima página)

76 Capítulo 1. Patterns

Page 81: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

7 */8 class Receiver9 {

10 private bool $enableDate = false;11

12 /**13 * @var string[]14 */15 private array $output = [];16

17 public function write(string $str)18 {19 if ($this->enableDate) {20 $str .= ' ['.date('Y-m-d').']';21 }22

23 $this->output[] = $str;24 }25

26 public function getOutput(): string27 {28 return join("\n", $this->output);29 }30

31 /**32 * Enable receiver to display message date33 */34 public function enableDate()35 {36 $this->enableDate = true;37 }38

39 /**40 * Disable receiver to display message date41 */42 public function disableDate()43 {44 $this->enableDate = false;45 }46 }

Invoker.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command;4

5 /**6 * Invoker is using the command given to it.7 * Example : an Application in SF2.8 */9 class Invoker

10 {11 private Command $command;12

13 /**14 * in the invoker we find this kind of method for subscribing the command

(continué en la próxima página)

1.3. Behavioral 77

Page 82: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

15 * There can be also a stack, a list, a fixed set ...16 */17 public function setCommand(Command $cmd)18 {19 $this->command = $cmd;20 }21

22 /**23 * executes the command; the invoker is the same whatever is the command24 */25 public function run()26 {27 $this->command->execute();28 }29 }

Test

Tests/CommandTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command\Tests;4

5 use DesignPatterns\Behavioral\Command\HelloCommand;6 use DesignPatterns\Behavioral\Command\Invoker;7 use DesignPatterns\Behavioral\Command\Receiver;8 use PHPUnit\Framework\TestCase;9

10 class CommandTest extends TestCase11 {12 public function testInvocation()13 {14 $invoker = new Invoker();15 $receiver = new Receiver();16

17 $invoker->setCommand(new HelloCommand($receiver));18 $invoker->run();19 $this->assertSame('Hello World', $receiver->getOutput());20 }21 }

Tests/UndoableCommandTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Command\Tests;4

5 use DesignPatterns\Behavioral\Command\AddMessageDateCommand;6 use DesignPatterns\Behavioral\Command\HelloCommand;7 use DesignPatterns\Behavioral\Command\Invoker;8 use DesignPatterns\Behavioral\Command\Receiver;9 use PHPUnit\Framework\TestCase;

10

11 class UndoableCommandTest extends TestCase

(continué en la próxima página)

78 Capítulo 1. Patterns

Page 83: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 {13 public function testInvocation()14 {15 $invoker = new Invoker();16 $receiver = new Receiver();17

18 $invoker->setCommand(new HelloCommand($receiver));19 $invoker->run();20 $this->assertSame('Hello World', $receiver->getOutput());21

22 $messageDateCommand = new AddMessageDateCommand($receiver);23 $messageDateCommand->execute();24

25 $invoker->run();26 $this->assertSame("Hello World\nHello World [".date('Y-m-d').']', $receiver->

→˓getOutput());27

28 $messageDateCommand->undo();29

30 $invoker->run();31 $this->assertSame("Hello World\nHello World [".date('Y-m-d')."]\nHello World",

→˓ $receiver->getOutput());32 }33 }

1.3.3 Iterator

Purpose

To make an object iterable and to make it appear like a collection of objects.

Examples

to process a file line by line by just running over all lines (which have an object representation) for a file (whichof course is an object, too)

Note

Standard PHP Library (SPL) defines an interface Iterator which is best suited for this! Often you would want toimplement the Countable interface too, to allow count($object) on your iterable object

1.3. Behavioral 79

Page 84: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Book.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Iterator;4

5 class Book6 {7 private string $author;8 private string $title;9

10 public function __construct(string $title, string $author)11 {12 $this->author = $author;13 $this->title = $title;14 }15

(continué en la próxima página)

80 Capítulo 1. Patterns

Page 85: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

16 public function getAuthor(): string17 {18 return $this->author;19 }20

21 public function getTitle(): string22 {23 return $this->title;24 }25

26 public function getAuthorAndTitle(): string27 {28 return $this->getTitle().' by '.$this->getAuthor();29 }30 }

BookList.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Iterator;4

5 use Countable;6 use Iterator;7

8 class BookList implements Countable, Iterator9 {

10 /**11 * @var Book[]12 */13 private array $books = [];14 private int $currentIndex = 0;15

16 public function addBook(Book $book)17 {18 $this->books[] = $book;19 }20

21 public function removeBook(Book $bookToRemove)22 {23 foreach ($this->books as $key => $book) {24 if ($book->getAuthorAndTitle() === $bookToRemove->getAuthorAndTitle()) {25 unset($this->books[$key]);26 }27 }28

29 $this->books = array_values($this->books);30 }31

32 public function count(): int33 {34 return count($this->books);35 }36

37 public function current(): Book38 {39 return $this->books[$this->currentIndex];

(continué en la próxima página)

1.3. Behavioral 81

Page 86: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

40 }41

42 public function key(): int43 {44 return $this->currentIndex;45 }46

47 public function next()48 {49 $this->currentIndex++;50 }51

52 public function rewind()53 {54 $this->currentIndex = 0;55 }56

57 public function valid(): bool58 {59 return isset($this->books[$this->currentIndex]);60 }61 }

Test

Tests/IteratorTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Iterator\Tests;4

5 use DesignPatterns\Behavioral\Iterator\Book;6 use DesignPatterns\Behavioral\Iterator\BookList;7 use PHPUnit\Framework\TestCase;8

9 class IteratorTest extends TestCase10 {11 public function testCanIterateOverBookList()12 {13 $bookList = new BookList();14 $bookList->addBook(new Book('Learning PHP Design Patterns', 'William Sanders

→˓'));15 $bookList->addBook(new Book('Professional Php Design Patterns', 'Aaron Saray

→˓'));16 $bookList->addBook(new Book('Clean Code', 'Robert C. Martin'));17

18 $books = [];19

20 foreach ($bookList as $book) {21 $books[] = $book->getAuthorAndTitle();22 }23

24 $this->assertSame(25 [26 'Learning PHP Design Patterns by William Sanders',

(continué en la próxima página)

82 Capítulo 1. Patterns

Page 87: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

27 'Professional Php Design Patterns by Aaron Saray',28 'Clean Code by Robert C. Martin',29 ],30 $books31 );32 }33

34 public function testCanIterateOverBookListAfterRemovingBook()35 {36 $book = new Book('Clean Code', 'Robert C. Martin');37 $book2 = new Book('Professional Php Design Patterns', 'Aaron Saray');38

39 $bookList = new BookList();40 $bookList->addBook($book);41 $bookList->addBook($book2);42 $bookList->removeBook($book);43

44 $books = [];45 foreach ($bookList as $book) {46 $books[] = $book->getAuthorAndTitle();47 }48

49 $this->assertSame(50 ['Professional Php Design Patterns by Aaron Saray'],51 $books52 );53 }54

55 public function testCanAddBookToList()56 {57 $book = new Book('Clean Code', 'Robert C. Martin');58

59 $bookList = new BookList();60 $bookList->addBook($book);61

62 $this->assertCount(1, $bookList);63 }64

65 public function testCanRemoveBookFromList()66 {67 $book = new Book('Clean Code', 'Robert C. Martin');68

69 $bookList = new BookList();70 $bookList->addBook($book);71 $bookList->removeBook($book);72

73 $this->assertCount(0, $bookList);74 }75 }

1.3.4 Mediator

1.3. Behavioral 83

Page 88: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Purpose

This pattern provides an easy way to decouple many components working together. It is a good alternative to ObserverIF you have a «central intelligence», like a controller (but not in the sense of the MVC).

All components (called Colleague) are only coupled to the Mediator interface and it is a good thing because in OOP,one good friend is better than many. This is the key-feature of this pattern.

UML Diagram

84 Capítulo 1. Patterns

Page 89: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

Mediator.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Mediator;4

5 interface Mediator6 {7 public function getUser(string $username): string;8 }

Colleague.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Mediator;4

5 abstract class Colleague6 {7 protected Mediator $mediator;8

9 public function setMediator(Mediator $mediator)10 {11 $this->mediator = $mediator;12 }13 }

Ui.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Mediator;4

5 class Ui extends Colleague6 {7 public function outputUserInfo(string $username)8 {9 echo $this->mediator->getUser($username);

10 }11 }

UserRepository.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Mediator;4

5 class UserRepository extends Colleague6 {7 public function getUserName(string $user): string8 {9 return 'User: ' . $user;

10 }11 }

1.3. Behavioral 85

Page 90: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UserRepositoryUiMediator.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Mediator;4

5 class UserRepositoryUiMediator implements Mediator6 {7 private UserRepository $userRepository;8 private Ui $ui;9

10 public function __construct(UserRepository $userRepository, Ui $ui)11 {12 $this->userRepository = $userRepository;13 $this->ui = $ui;14

15 $this->userRepository->setMediator($this);16 $this->ui->setMediator($this);17 }18

19 public function printInfoAbout(string $user)20 {21 $this->ui->outputUserInfo($user);22 }23

24 public function getUser(string $username): string25 {26 return $this->userRepository->getUserName($username);27 }28 }

Test

Tests/MediatorTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Tests\Mediator\Tests;4

5 use DesignPatterns\Behavioral\Mediator\Ui;6 use DesignPatterns\Behavioral\Mediator\UserRepository;7 use DesignPatterns\Behavioral\Mediator\UserRepositoryUiMediator;8 use PHPUnit\Framework\TestCase;9

10 class MediatorTest extends TestCase11 {12 public function testOutputHelloWorld()13 {14 $mediator = new UserRepositoryUiMediator(new UserRepository(), new Ui());15

16 $this->expectOutputString('User: Dominik');17 $mediator->printInfoAbout('Dominik');18 }19 }

86 Capítulo 1. Patterns

Page 91: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1.3.5 Memento

Purpose

It provides the ability to restore an object to it’s previous state (undo via rollback) or to gain access to state of theobject, without revealing it’s implementation (i.e., the object is not required to have a function to return the currentstate).

The memento pattern is implemented with three objects: the Originator, a Caretaker and a Memento.

Memento – an object that contains a concrete unique snapshot of state of any object or resource: string, number, array,an instance of class and so on. The uniqueness in this case does not imply the prohibition existence of similar states indifferent snapshots. That means the state can be extracted as the independent clone. Any object stored in the Mementoshould be a full copy of the original object rather than a reference to the original object. The Memento object is a«opaque object» (the object that no one can or should change).

Originator – it is an object that contains the actual state of an external object is strictly specified type. Originatoris able to create a unique copy of this state and return it wrapped in a Memento. The Originator does not know thehistory of changes. You can set a concrete state to Originator from the outside, which will be considered as actual. TheOriginator must make sure that given state corresponds the allowed type of object. Originator may (but not should)have any methods, but they they can’t make changes to the saved object state.

Caretaker controls the states history. He may make changes to an object; take a decision to save the state of an externalobject in the Originator; ask from the Originator snapshot of the current state; or set the Originator state to equivalencewith some snapshot from history.

Examples

The seed of a pseudorandom number generator

The state in a finite state machine

Control for intermediate states of ORM Model before saving

1.3. Behavioral 87

Page 92: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Memento.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Memento;4

5 class Memento(continué en la próxima página)

88 Capítulo 1. Patterns

Page 93: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

6 {7 private State $state;8

9 public function __construct(State $stateToSave)10 {11 $this->state = $stateToSave;12 }13

14 public function getState(): State15 {16 return $this->state;17 }18 }

State.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Memento;4

5 use InvalidArgumentException;6

7 class State8 {9 const STATE_CREATED = 'created';

10 const STATE_OPENED = 'opened';11 const STATE_ASSIGNED = 'assigned';12 const STATE_CLOSED = 'closed';13

14 private string $state;15

16 /**17 * @var string[]18 */19 private static array $validStates = [20 self::STATE_CREATED,21 self::STATE_OPENED,22 self::STATE_ASSIGNED,23 self::STATE_CLOSED,24 ];25

26 public function __construct(string $state)27 {28 self::ensureIsValidState($state);29

30 $this->state = $state;31 }32

33 private static function ensureIsValidState(string $state)34 {35 if (!in_array($state, self::$validStates)) {36 throw new InvalidArgumentException('Invalid state given');37 }38 }39

40 public function __toString(): string41 {

(continué en la próxima página)

1.3. Behavioral 89

Page 94: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

42 return $this->state;43 }44 }

Ticket.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Memento;4

5 /**6 * Ticket is the "Originator" in this implementation7 */8 class Ticket9 {

10 private State $currentState;11

12 public function __construct()13 {14 $this->currentState = new State(State::STATE_CREATED);15 }16

17 public function open()18 {19 $this->currentState = new State(State::STATE_OPENED);20 }21

22 public function assign()23 {24 $this->currentState = new State(State::STATE_ASSIGNED);25 }26

27 public function close()28 {29 $this->currentState = new State(State::STATE_CLOSED);30 }31

32 public function saveToMemento(): Memento33 {34 return new Memento(clone $this->currentState);35 }36

37 public function restoreFromMemento(Memento $memento)38 {39 $this->currentState = $memento->getState();40 }41

42 public function getState(): State43 {44 return $this->currentState;45 }46 }

Test

Tests/MementoTest.php

90 Capítulo 1. Patterns

Page 95: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Memento\Tests;4

5 use DesignPatterns\Behavioral\Memento\State;6 use DesignPatterns\Behavioral\Memento\Ticket;7 use PHPUnit\Framework\TestCase;8

9 class MementoTest extends TestCase10 {11 public function testOpenTicketAssignAndSetBackToOpen()12 {13 $ticket = new Ticket();14

15 // open the ticket16 $ticket->open();17 $openedState = $ticket->getState();18 $this->assertSame(State::STATE_OPENED, (string) $ticket->getState());19

20 $memento = $ticket->saveToMemento();21

22 // assign the ticket23 $ticket->assign();24 $this->assertSame(State::STATE_ASSIGNED, (string) $ticket->getState());25

26 // now restore to the opened state, but verify that the state object has been→˓cloned for the memento

27 $ticket->restoreFromMemento($memento);28

29 $this->assertSame(State::STATE_OPENED, (string) $ticket->getState());30 $this->assertNotSame($openedState, $ticket->getState());31 }32 }

1.3.6 Null Object

Purpose

NullObject is not a GoF design pattern but a schema which appears frequently enough to be considered a pattern. Ithas the following benefits:

Client code is simplified

Reduces the chance of null pointer exceptions

Fewer conditionals require less test cases

Methods that return an object or null should instead return an object or NullObject. NullObjectssimplify boilerplate code such as if (!is_null($obj)) { $obj->callSomething(); } to just$obj->callSomething(); by eliminating the conditional check in client code.

Examples

Null logger or null output to preserve a standard way of interaction between objects, even if the shouldn’t doanything

1.3. Behavioral 91

Page 96: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

null handler in a Chain of Responsibilities pattern

null command in a Command pattern

92 Capítulo 1. Patterns

Page 97: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

1.3. Behavioral 93

Page 98: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

Service.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\NullObject;4

5 class Service6 {7 private Logger $logger;8

9 public function __construct(Logger $logger)10 {11 $this->logger = $logger;12 }13

14 /**15 * do something ...16 */17 public function doSomething()18 {19 // notice here that you don't have to check if the logger is set with eg. is_

→˓null(), instead just use it20 $this->logger->log('We are in '.__METHOD__);21 }22 }

Logger.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\NullObject;4

5 /**6 * Key feature: NullLogger must inherit from this interface like any other loggers7 */8 interface Logger9 {

10 public function log(string $str);11 }

PrintLogger.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\NullObject;4

5 class PrintLogger implements Logger6 {7 public function log(string $str)8 {9 echo $str;

10 }11 }

NullLogger.php

94 Capítulo 1. Patterns

Page 99: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\NullObject;4

5 class NullLogger implements Logger6 {7 public function log(string $str)8 {9 // do nothing

10 }11 }

Test

Tests/LoggerTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\NullObject\Tests;4

5 use DesignPatterns\Behavioral\NullObject\NullLogger;6 use DesignPatterns\Behavioral\NullObject\PrintLogger;7 use DesignPatterns\Behavioral\NullObject\Service;8 use PHPUnit\Framework\TestCase;9

10 class LoggerTest extends TestCase11 {12 public function testNullObject()13 {14 $service = new Service(new NullLogger());15 $this->expectOutputString('');16 $service->doSomething();17 }18

19 public function testStandardLogger()20 {21 $service = new Service(new PrintLogger());22 $this->expectOutputString('We are in

→˓DesignPatterns\Behavioral\NullObject\Service::doSomething');23 $service->doSomething();24 }25 }

1.3.7 Observer

Purpose

To implement a publish/subscribe behaviour to an object, whenever a «Subject» object changes its state, the attached«Observers» will be notified. It is used to shorten the amount of coupled objects and uses loose coupling instead.

Examples

a message queue system is observed to show the progress of a job in a GUI

1.3. Behavioral 95

Page 100: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Note

PHP already defines two interfaces that can help to implement this pattern: SplObserver and SplSubject.

UML Diagram

Code

You can also find this code on GitHub

User.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Observer;4

5 use SplSubject;6 use SplObjectStorage;7 use SplObserver;8

9 /**10 * User implements the observed object (called Subject), it maintains a list of

→˓observers and sends notifications to11 * them in case changes are made on the User object12 */13 class User implements SplSubject14 {15 private string $email;16 private SplObjectStorage $observers;17

18 public function __construct()19 {20 $this->observers = new SplObjectStorage();

(continué en la próxima página)

96 Capítulo 1. Patterns

Page 101: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

21 }22

23 public function attach(SplObserver $observer)24 {25 $this->observers->attach($observer);26 }27

28 public function detach(SplObserver $observer)29 {30 $this->observers->detach($observer);31 }32

33 public function changeEmail(string $email)34 {35 $this->email = $email;36 $this->notify();37 }38

39 public function notify()40 {41 /** @var SplObserver $observer */42 foreach ($this->observers as $observer) {43 $observer->update($this);44 }45 }46 }

UserObserver.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Observer;4

5 use SplObserver;6 use SplSubject;7

8 class UserObserver implements SplObserver9 {

10 /**11 * @var SplSubject[]12 */13 private array $changedUsers = [];14

15 /**16 * It is called by the Subject, usually by SplSubject::notify()17 */18 public function update(SplSubject $subject)19 {20 $this->changedUsers[] = clone $subject;21 }22

23 /**24 * @return SplSubject[]25 */26 public function getChangedUsers(): array27 {28 return $this->changedUsers;

(continué en la próxima página)

1.3. Behavioral 97

Page 102: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

29 }30 }

Test

Tests/ObserverTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Observer\Tests;4

5 use DesignPatterns\Behavioral\Observer\User;6 use DesignPatterns\Behavioral\Observer\UserObserver;7 use PHPUnit\Framework\TestCase;8

9 class ObserverTest extends TestCase10 {11 public function testChangeInUserLeadsToUserObserverBeingNotified()12 {13 $observer = new UserObserver();14

15 $user = new User();16 $user->attach($observer);17

18 $user->changeEmail('[email protected]');19 $this->assertCount(1, $observer->getChangedUsers());20 }21 }

1.3.8 Specification

Purpose

Builds a clear specification of business rules, where objects can be checked against. The composite specification classhas one method called isSatisfiedBy that returns either true or false depending on whether the given objectsatisfies the specification.

Examples

RulerZ

98 Capítulo 1. Patterns

Page 103: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Item.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Specification;4

5 class Item6 {7 private float $price;8

9 public function __construct(float $price)10 {11 $this->price = $price;12 }13

14 public function getPrice(): float15 {16 return $this->price;17 }18 }

Specification.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Specification;4

5 interface Specification6 {7 public function isSatisfiedBy(Item $item): bool;8 }

OrSpecification.php

1.3. Behavioral 99

Page 104: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Specification;4

5 class OrSpecification implements Specification6 {7 /**8 * @var Specification[]9 */

10 private array $specifications;11

12 /**13 * @param Specification[] $specifications14 */15 public function __construct(Specification ...$specifications)16 {17 $this->specifications = $specifications;18 }19

20 /*21 * if at least one specification is true, return true, else return false22 */23 public function isSatisfiedBy(Item $item): bool24 {25 foreach ($this->specifications as $specification) {26 if ($specification->isSatisfiedBy($item)) {27 return true;28 }29 }30

31 return false;32 }33 }

PriceSpecification.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Specification;4

5 class PriceSpecification implements Specification6 {7 private ?float $maxPrice;8 private ?float $minPrice;9

10 public function __construct(?float $minPrice, ?float $maxPrice)11 {12 $this->minPrice = $minPrice;13 $this->maxPrice = $maxPrice;14 }15

16 public function isSatisfiedBy(Item $item): bool17 {18 if ($this->maxPrice !== null && $item->getPrice() > $this->maxPrice) {19 return false;20 }21

(continué en la próxima página)

100 Capítulo 1. Patterns

Page 105: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

22 if ($this->minPrice !== null && $item->getPrice() < $this->minPrice) {23 return false;24 }25

26 return true;27 }28 }

AndSpecification.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Specification;4

5 class AndSpecification implements Specification6 {7 /**8 * @var Specification[]9 */

10 private array $specifications;11

12 /**13 * @param Specification[] $specifications14 */15 public function __construct(Specification ...$specifications)16 {17 $this->specifications = $specifications;18 }19

20 /**21 * if at least one specification is false, return false, else return true.22 */23 public function isSatisfiedBy(Item $item): bool24 {25 foreach ($this->specifications as $specification) {26 if (!$specification->isSatisfiedBy($item)) {27 return false;28 }29 }30

31 return true;32 }33 }

NotSpecification.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Specification;4

5 class NotSpecification implements Specification6 {7 private Specification $specification;8

9 public function __construct(Specification $specification)10 {11 $this->specification = $specification;

(continué en la próxima página)

1.3. Behavioral 101

Page 106: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 }13

14 public function isSatisfiedBy(Item $item): bool15 {16 return !$this->specification->isSatisfiedBy($item);17 }18 }

Test

Tests/SpecificationTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Specification\Tests;4

5 use DesignPatterns\Behavioral\Specification\Item;6 use DesignPatterns\Behavioral\Specification\NotSpecification;7 use DesignPatterns\Behavioral\Specification\OrSpecification;8 use DesignPatterns\Behavioral\Specification\AndSpecification;9 use DesignPatterns\Behavioral\Specification\PriceSpecification;

10 use PHPUnit\Framework\TestCase;11

12 class SpecificationTest extends TestCase13 {14 public function testCanOr()15 {16 $spec1 = new PriceSpecification(50, 99);17 $spec2 = new PriceSpecification(101, 200);18

19 $orSpec = new OrSpecification($spec1, $spec2);20

21 $this->assertFalse($orSpec->isSatisfiedBy(new Item(100)));22 $this->assertTrue($orSpec->isSatisfiedBy(new Item(51)));23 $this->assertTrue($orSpec->isSatisfiedBy(new Item(150)));24 }25

26 public function testCanAnd()27 {28 $spec1 = new PriceSpecification(50, 100);29 $spec2 = new PriceSpecification(80, 200);30

31 $andSpec = new AndSpecification($spec1, $spec2);32

33 $this->assertFalse($andSpec->isSatisfiedBy(new Item(150)));34 $this->assertFalse($andSpec->isSatisfiedBy(new Item(1)));35 $this->assertFalse($andSpec->isSatisfiedBy(new Item(51)));36 $this->assertTrue($andSpec->isSatisfiedBy(new Item(100)));37 }38

39 public function testCanNot()40 {41 $spec1 = new PriceSpecification(50, 100);42 $notSpec = new NotSpecification($spec1);43

(continué en la próxima página)

102 Capítulo 1. Patterns

Page 107: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

44 $this->assertTrue($notSpec->isSatisfiedBy(new Item(150)));45 $this->assertFalse($notSpec->isSatisfiedBy(new Item(50)));46 }47 }

1.3.9 State

Purpose

Encapsulate varying behavior for the same routine based on an object’s state. This can be a cleaner way for an objectto change its behavior at runtime without resorting to large monolithic conditional statements.

UML Diagram

Code

You can also find this code on GitHub

OrderContext.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\State;4

5 class OrderContext(continué en la próxima página)

1.3. Behavioral 103

Page 108: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

6 {7 private State $state;8

9 public static function create(): OrderContext10 {11 $order = new self();12 $order->state = new StateCreated();13

14 return $order;15 }16

17 public function setState(State $state)18 {19 $this->state = $state;20 }21

22 public function proceedToNext()23 {24 $this->state->proceedToNext($this);25 }26

27 public function toString()28 {29 return $this->state->toString();30 }31 }

State.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\State;4

5 interface State6 {7 public function proceedToNext(OrderContext $context);8

9 public function toString(): string;10 }

StateCreated.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\State;4

5 class StateCreated implements State6 {7 public function proceedToNext(OrderContext $context)8 {9 $context->setState(new StateShipped());

10 }11

12 public function toString(): string13 {14 return 'created';15 }

(continué en la próxima página)

104 Capítulo 1. Patterns

Page 109: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

16 }

StateShipped.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\State;4

5 class StateShipped implements State6 {7 public function proceedToNext(OrderContext $context)8 {9 $context->setState(new StateDone());

10 }11

12 public function toString(): string13 {14 return 'shipped';15 }16 }

StateDone.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\State;4

5 class StateDone implements State6 {7 public function proceedToNext(OrderContext $context)8 {9 // there is nothing more to do

10 }11

12 public function toString(): string13 {14 return 'done';15 }16 }

Test

Tests/StateTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\State\Tests;4

5 use DesignPatterns\Behavioral\State\OrderContext;6 use PHPUnit\Framework\TestCase;7

8 class StateTest extends TestCase9 {

10 public function testIsCreatedWithStateCreated()11 {

(continué en la próxima página)

1.3. Behavioral 105

Page 110: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 $orderContext = OrderContext::create();13

14 $this->assertSame('created', $orderContext->toString());15 }16

17 public function testCanProceedToStateShipped()18 {19 $contextOrder = OrderContext::create();20 $contextOrder->proceedToNext();21

22 $this->assertSame('shipped', $contextOrder->toString());23 }24

25 public function testCanProceedToStateDone()26 {27 $contextOrder = OrderContext::create();28 $contextOrder->proceedToNext();29 $contextOrder->proceedToNext();30

31 $this->assertSame('done', $contextOrder->toString());32 }33

34 public function testStateDoneIsTheLastPossibleState()35 {36 $contextOrder = OrderContext::create();37 $contextOrder->proceedToNext();38 $contextOrder->proceedToNext();39 $contextOrder->proceedToNext();40

41 $this->assertSame('done', $contextOrder->toString());42 }43 }

1.3.10 Strategy

Terminology:

Context

Strategy

Concrete Strategy

Purpose

To separate strategies and to enable fast switching between them. Also this pattern is a good alternative to inheritance(instead of having an abstract class that is extended).

Examples

sorting a list of objects, one strategy by date, the other by id

simplify unit testing: e.g. switching between file and in-memory storage

106 Capítulo 1. Patterns

Page 111: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Context.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Strategy;4

5 class Context(continué en la próxima página)

1.3. Behavioral 107

Page 112: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

6 {7 private Comparator $comparator;8

9 public function __construct(Comparator $comparator)10 {11 $this->comparator = $comparator;12 }13

14 public function executeStrategy(array $elements): array15 {16 uasort($elements, [$this->comparator, 'compare']);17

18 return $elements;19 }20 }

Comparator.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Strategy;4

5 interface Comparator6 {7 /**8 * @param mixed $a9 * @param mixed $b

10 *11 * @return int12 */13 public function compare($a, $b): int;14 }

DateComparator.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Strategy;4

5 use DateTime;6

7 class DateComparator implements Comparator8 {9 public function compare($a, $b): int

10 {11 $aDate = new DateTime($a['date']);12 $bDate = new DateTime($b['date']);13

14 return $aDate <=> $bDate;15 }16 }

IdComparator.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Strategy;(continué en la próxima página)

108 Capítulo 1. Patterns

Page 113: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

4

5 class IdComparator implements Comparator6 {7 public function compare($a, $b): int8 {9 return $a['id'] <=> $b['id'];

10 }11 }

Test

Tests/StrategyTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Strategy\Tests;4

5 use DesignPatterns\Behavioral\Strategy\Context;6 use DesignPatterns\Behavioral\Strategy\DateComparator;7 use DesignPatterns\Behavioral\Strategy\IdComparator;8 use PHPUnit\Framework\TestCase;9

10 class StrategyTest extends TestCase11 {12 public function provideIntegers()13 {14 return [15 [16 [['id' => 2], ['id' => 1], ['id' => 3]],17 ['id' => 1],18 ],19 [20 [['id' => 3], ['id' => 2], ['id' => 1]],21 ['id' => 1],22 ],23 ];24 }25

26 public function provideDates()27 {28 return [29 [30 [['date' => '2014-03-03'], ['date' => '2015-03-02'], ['date' => '2013-

→˓03-01']],31 ['date' => '2013-03-01'],32 ],33 [34 [['date' => '2014-02-03'], ['date' => '2013-02-01'], ['date' => '2015-

→˓02-02']],35 ['date' => '2013-02-01'],36 ],37 ];38 }39

40 /**(continué en la próxima página)

1.3. Behavioral 109

Page 114: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

41 * @dataProvider provideIntegers42 *43 * @param array $collection44 * @param array $expected45 */46 public function testIdComparator($collection, $expected)47 {48 $obj = new Context(new IdComparator());49 $elements = $obj->executeStrategy($collection);50

51 $firstElement = array_shift($elements);52 $this->assertSame($expected, $firstElement);53 }54

55 /**56 * @dataProvider provideDates57 *58 * @param array $collection59 * @param array $expected60 */61 public function testDateComparator($collection, $expected)62 {63 $obj = new Context(new DateComparator());64 $elements = $obj->executeStrategy($collection);65

66 $firstElement = array_shift($elements);67 $this->assertSame($expected, $firstElement);68 }69 }

1.3.11 Template Method

Purpose

Template Method is a behavioral design pattern.

Perhaps you have encountered it many times already. The idea is to let subclasses of this abstract template «finish» thebehavior of an algorithm.

A.k.a the «Hollywood principle»: «Don’t call us, we call you.» This class is not called by subclasses but the inverse.How? With abstraction of course.

In other words, this is a skeleton of algorithm, well-suited for framework libraries. The user has just to implement onemethod and the superclass do the job.

It is an easy way to decouple concrete classes and reduce copy-paste, that’s why you’ll find it everywhere.

110 Capítulo 1. Patterns

Page 115: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Journey.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\TemplateMethod;4

5 abstract class Journey6 {7 /**8 * @var string[]

(continué en la próxima página)

1.3. Behavioral 111

Page 116: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

9 */10 private array $thingsToDo = [];11

12 /**13 * This is the public service provided by this class and its subclasses.14 * Notice it is final to "freeze" the global behavior of algorithm.15 * If you want to override this contract, make an interface with only takeATrip()16 * and subclass it.17 */18 final public function takeATrip()19 {20 $this->thingsToDo[] = $this->buyAFlight();21 $this->thingsToDo[] = $this->takePlane();22 $this->thingsToDo[] = $this->enjoyVacation();23 $buyGift = $this->buyGift();24

25 if ($buyGift !== null) {26 $this->thingsToDo[] = $buyGift;27 }28

29 $this->thingsToDo[] = $this->takePlane();30 }31

32 /**33 * This method must be implemented, this is the key-feature of this pattern.34 */35 abstract protected function enjoyVacation(): string;36

37 /**38 * This method is also part of the algorithm but it is optional.39 * You can override it only if you need to40 */41 protected function buyGift(): ?string42 {43 return null;44 }45

46 private function buyAFlight(): string47 {48 return 'Buy a flight ticket';49 }50

51 private function takePlane(): string52 {53 return 'Taking the plane';54 }55

56 /**57 * @return string[]58 */59 public function getThingsToDo(): array60 {61 return $this->thingsToDo;62 }63 }

BeachJourney.php

112 Capítulo 1. Patterns

Page 117: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\TemplateMethod;4

5 class BeachJourney extends Journey6 {7 protected function enjoyVacation(): string8 {9 return "Swimming and sun-bathing";

10 }11 }

CityJourney.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\TemplateMethod;4

5 class CityJourney extends Journey6 {7 protected function enjoyVacation(): string8 {9 return "Eat, drink, take photos and sleep";

10 }11

12 protected function buyGift(): ?string13 {14 return "Buy a gift";15 }16 }

Test

Tests/JourneyTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\TemplateMethod\Tests;4

5 use DesignPatterns\Behavioral\TemplateMethod\BeachJourney;6 use DesignPatterns\Behavioral\TemplateMethod\CityJourney;7 use PHPUnit\Framework\TestCase;8

9 class JourneyTest extends TestCase10 {11 public function testCanGetOnVacationOnTheBeach()12 {13 $beachJourney = new BeachJourney();14 $beachJourney->takeATrip();15

16 $this->assertSame(17 ['Buy a flight ticket', 'Taking the plane', 'Swimming and sun-bathing',

→˓'Taking the plane'],18 $beachJourney->getThingsToDo()19 );20 }

(continué en la próxima página)

1.3. Behavioral 113

Page 118: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

21

22 public function testCanGetOnAJourneyToACity()23 {24 $cityJourney = new CityJourney();25 $cityJourney->takeATrip();26

27 $this->assertSame(28 [29 'Buy a flight ticket',30 'Taking the plane',31 'Eat, drink, take photos and sleep',32 'Buy a gift',33 'Taking the plane'34 ],35 $cityJourney->getThingsToDo()36 );37 }38 }

1.3.12 Visitor

Purpose

The Visitor Pattern lets you outsource operations on objects to other objects. The main reason to do this is to keepa separation of concerns. But classes have to define a contract to allow visitors (the Role::accept method in theexample).

The contract is an abstract class but you can have also a clean interface. In that case, each Visitor has to choose itselfwhich method to invoke on the visitor.

114 Capítulo 1. Patterns

Page 119: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

1.3. Behavioral 115

Page 120: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

RoleVisitor.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Visitor;4

5 /**6 * Note: the visitor must not choose itself which method to7 * invoke, it is the visited object that makes this decision8 */9 interface RoleVisitor

10 {11 public function visitUser(User $role);12

13 public function visitGroup(Group $role);14 }

RecordingVisitor.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Visitor;4

5 class RecordingVisitor implements RoleVisitor6 {7 /**8 * @var Role[]9 */

10 private array $visited = [];11

12 public function visitGroup(Group $role)13 {14 $this->visited[] = $role;15 }16

17 public function visitUser(User $role)18 {19 $this->visited[] = $role;20 }21

22 /**23 * @return Role[]24 */25 public function getVisited(): array26 {27 return $this->visited;28 }29 }

Role.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Visitor;

(continué en la próxima página)

116 Capítulo 1. Patterns

Page 121: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

4

5 interface Role6 {7 public function accept(RoleVisitor $visitor);8 }

User.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Visitor;4

5 class User implements Role6 {7 private string $name;8

9 public function __construct(string $name)10 {11 $this->name = $name;12 }13

14 public function getName(): string15 {16 return sprintf('User %s', $this->name);17 }18

19 public function accept(RoleVisitor $visitor)20 {21 $visitor->visitUser($this);22 }23 }

Group.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Behavioral\Visitor;4

5 class Group implements Role6 {7 private string $name;8

9 public function __construct(string $name)10 {11 $this->name = $name;12 }13

14 public function getName(): string15 {16 return sprintf('Group: %s', $this->name);17 }18

19 public function accept(RoleVisitor $visitor)20 {21 $visitor->visitGroup($this);22 }23 }

1.3. Behavioral 117

Page 122: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Test

Tests/VisitorTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\Tests\Visitor\Tests;4

5 use DesignPatterns\Behavioral\Visitor\RecordingVisitor;6 use DesignPatterns\Behavioral\Visitor\User;7 use DesignPatterns\Behavioral\Visitor\Group;8 use DesignPatterns\Behavioral\Visitor\Role;9 use DesignPatterns\Behavioral\Visitor;

10 use PHPUnit\Framework\TestCase;11

12 class VisitorTest extends TestCase13 {14 private RecordingVisitor $visitor;15

16 protected function setUp(): void17 {18 $this->visitor = new RecordingVisitor();19 }20

21 public function provideRoles()22 {23 return [24 [new User('Dominik')],25 [new Group('Administrators')],26 ];27 }28

29 /**30 * @dataProvider provideRoles31 */32 public function testVisitSomeRole(Role $role)33 {34 $role->accept($this->visitor);35 $this->assertSame($role, $this->visitor->getVisited()[0]);36 }37 }

1.4 More

1.4.1 Service Locator

THIS IS CONSIDERED TO BE AN ANTI-PATTERN!

Service Locator is considered for some people an anti-pattern. It violates the Dependency Inversion principle. ServiceLocator hides class” dependencies instead of exposing them as you would do using the Dependency Injection. In caseof changes of those dependencies you risk to break the functionality of classes which are using them, making yoursystem difficult to maintain.

118 Capítulo 1. Patterns

Page 123: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Purpose

To implement a loosely coupled architecture in order to get better testable, maintainable and extendable code. DIpattern and Service Locator pattern are an implementation of the Inverse of Control pattern.

Usage

With ServiceLocator you can register a service for a given interface. By using the interface you can retrieve theservice and use it in the classes of the application without knowing its implementation. You can configure and injectthe Service Locator object on bootstrap.

1.4. More 119

Page 124: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

120 Capítulo 1. Patterns

Page 125: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

Code

You can also find this code on GitHub

Service.php

1 <?php2

3 namespace DesignPatterns\More\ServiceLocator;4

5 interface Service6 {7

8 }

ServiceLocator.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\ServiceLocator;4

5 use OutOfRangeException;6 use InvalidArgumentException;7

8 class ServiceLocator9 {

10 /**11 * @var string[][]12 */13 private array $services = [];14

15 /**16 * @var Service[]17 */18 private array $instantiated = [];19

20 public function addInstance(string $class, Service $service)21 {22 $this->instantiated[$class] = $service;23 }24

25 public function addClass(string $class, array $params)26 {27 $this->services[$class] = $params;28 }29

30 public function has(string $interface): bool31 {32 return isset($this->services[$interface]) || isset($this->instantiated[

→˓$interface]);33 }34

35 public function get(string $class): Service36 {37 if (isset($this->instantiated[$class])) {38 return $this->instantiated[$class];39 }40

(continué en la próxima página)

1.4. More 121

Page 126: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

41 $args = $this->services[$class];42

43 switch (count($args)) {44 case 0:45 $object = new $class();46 break;47 case 1:48 $object = new $class($args[0]);49 break;50 case 2:51 $object = new $class($args[0], $args[1]);52 break;53 case 3:54 $object = new $class($args[0], $args[1], $args[2]);55 break;56 default:57 throw new OutOfRangeException('Too many arguments given');58 }59

60 if (!$object instanceof Service) {61 throw new InvalidArgumentException('Could not register service: is no

→˓instance of Service');62 }63

64 $this->instantiated[$class] = $object;65

66 return $object;67 }68 }

LogService.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\ServiceLocator;4

5 class LogService implements Service6 {7

8 }

Test

Tests/ServiceLocatorTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\ServiceLocator\Tests;4

5 use DesignPatterns\More\ServiceLocator\LogService;6 use DesignPatterns\More\ServiceLocator\ServiceLocator;7 use PHPUnit\Framework\TestCase;8

9 class ServiceLocatorTest extends TestCase10 {

(continué en la próxima página)

122 Capítulo 1. Patterns

Page 127: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

11 private ServiceLocator $serviceLocator;12

13 public function setUp(): void14 {15 $this->serviceLocator = new ServiceLocator();16 }17

18 public function testHasServices()19 {20 $this->serviceLocator->addInstance(LogService::class, new LogService());21

22 $this->assertTrue($this->serviceLocator->has(LogService::class));23 $this->assertFalse($this->serviceLocator->has(self::class));24 }25

26 public function testGetWillInstantiateLogServiceIfNoInstanceHasBeenCreatedYet()27 {28 $this->serviceLocator->addClass(LogService::class, []);29 $logger = $this->serviceLocator->get(LogService::class);30

31 $this->assertInstanceOf(LogService::class, $logger);32 }33 }

1.4.2 Repository

Purpose

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providinga more object-oriented view of the persistence layer. Repository also supports the objective of achieving a cleanseparation and one-way dependency between the domain and data mapping layers.

Examples

Doctrine 2 ORM: there is Repository that mediates between Entity and DBAL and contains methods to retrieveobjects

Laravel Framework

1.4. More 123

Page 128: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Post.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\Repository\Domain;4

5 class Post6 {7 private PostId $id;8 private PostStatus $status;9 private string $title;

10 private string $text;11

(continué en la próxima página)

124 Capítulo 1. Patterns

Page 129: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

12 public static function draft(PostId $id, string $title, string $text): Post13 {14 return new self(15 $id,16 PostStatus::fromString(PostStatus::STATE_DRAFT),17 $title,18 $text19 );20 }21

22 public static function fromState(array $state): Post23 {24 return new self(25 PostId::fromInt($state['id']),26 PostStatus::fromInt($state['statusId']),27 $state['title'],28 $state['text']29 );30 }31

32 private function __construct(PostId $id, PostStatus $status, string $title,→˓string $text)

33 {34 $this->id = $id;35 $this->status = $status;36 $this->text = $text;37 $this->title = $title;38 }39

40 public function getId(): PostId41 {42 return $this->id;43 }44

45 public function getStatus(): PostStatus46 {47 return $this->status;48 }49

50 public function getText(): string51 {52 return $this->text;53 }54

55 public function getTitle(): string56 {57 return $this->title;58 }59 }

PostId.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\Repository\Domain;4

5 use InvalidArgumentException;(continué en la próxima página)

1.4. More 125

Page 130: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

6

7 /**8 * This is a perfect example of a value object that is identifiable by it's value

→˓alone and9 * is guaranteed to be valid each time an instance is created. Another important

→˓property of value objects10 * is immutability.11 *12 * Notice also the use of a named constructor (fromInt) which adds a little context

→˓when creating an instance.13 */14 class PostId15 {16 private int $id;17

18 public static function fromInt(int $id): PostId19 {20 self::ensureIsValid($id);21

22 return new self($id);23 }24

25 private function __construct(int $id)26 {27 $this->id = $id;28 }29

30 public function toInt(): int31 {32 return $this->id;33 }34

35 private static function ensureIsValid(int $id)36 {37 if ($id <= 0) {38 throw new InvalidArgumentException('Invalid PostId given');39 }40 }41 }

PostStatus.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\Repository\Domain;4

5 use InvalidArgumentException;6

7 /**8 * Like PostId, this is a value object which holds the value of the current status of

→˓a Post. It can be constructed9 * either from a string or int and is able to validate itself. An instance can then

→˓be converted back to int or string.10 */11 class PostStatus12 {13 const STATE_DRAFT_ID = 1;

(continué en la próxima página)

126 Capítulo 1. Patterns

Page 131: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

14 const STATE_PUBLISHED_ID = 2;15

16 const STATE_DRAFT = 'draft';17 const STATE_PUBLISHED = 'published';18

19 private static array $validStates = [20 self::STATE_DRAFT_ID => self::STATE_DRAFT,21 self::STATE_PUBLISHED_ID => self::STATE_PUBLISHED,22 ];23

24 private int $id;25 private string $name;26

27 public static function fromInt(int $statusId)28 {29 self::ensureIsValidId($statusId);30

31 return new self($statusId, self::$validStates[$statusId]);32 }33

34 public static function fromString(string $status)35 {36 self::ensureIsValidName($status);37 $state = array_search($status, self::$validStates);38

39 if ($state === false) {40 throw new InvalidArgumentException('Invalid state given!');41 }42

43 return new self($state, $status);44 }45

46 private function __construct(int $id, string $name)47 {48 $this->id = $id;49 $this->name = $name;50 }51

52 public function toInt(): int53 {54 return $this->id;55 }56

57 /**58 * there is a reason that I avoid using __toString() as it operates outside of

→˓the stack in PHP59 * and is therefor not able to operate well with exceptions60 */61 public function toString(): string62 {63 return $this->name;64 }65

66 private static function ensureIsValidId(int $status)67 {68 if (!in_array($status, array_keys(self::$validStates), true)) {69 throw new InvalidArgumentException('Invalid status id given');

(continué en la próxima página)

1.4. More 127

Page 132: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

70 }71 }72

73

74 private static function ensureIsValidName(string $status)75 {76 if (!in_array($status, self::$validStates, true)) {77 throw new InvalidArgumentException('Invalid status name given');78 }79 }80 }

PostRepository.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\Repository;4

5 use OutOfBoundsException;6 use DesignPatterns\More\Repository\Domain\Post;7 use DesignPatterns\More\Repository\Domain\PostId;8

9 /**10 * This class is situated between Entity layer (class Post) and access object layer

→˓(Persistence).11 *12 * Repository encapsulates the set of objects persisted in a data store and the

→˓operations performed over them13 * providing a more object-oriented view of the persistence layer14 *15 * Repository also supports the objective of achieving a clean separation and one-way

→˓dependency16 * between the domain and data mapping layers17 */18 class PostRepository19 {20 private Persistence $persistence;21

22 public function __construct(Persistence $persistence)23 {24 $this->persistence = $persistence;25 }26

27 public function generateId(): PostId28 {29 return PostId::fromInt($this->persistence->generateId());30 }31

32 public function findById(PostId $id): Post33 {34 try {35 $arrayData = $this->persistence->retrieve($id->toInt());36 } catch (OutOfBoundsException $e) {37 throw new OutOfBoundsException(sprintf('Post with id %d does not exist',

→˓$id->toInt()), 0, $e);38 }39

(continué en la próxima página)

128 Capítulo 1. Patterns

Page 133: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

40 return Post::fromState($arrayData);41 }42

43 public function save(Post $post)44 {45 $this->persistence->persist([46 'id' => $post->getId()->toInt(),47 'statusId' => $post->getStatus()->toInt(),48 'text' => $post->getText(),49 'title' => $post->getTitle(),50 ]);51 }52 }

Persistence.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\Repository;4

5 interface Persistence6 {7 public function generateId(): int;8

9 public function persist(array $data);10

11 public function retrieve(int $id): array;12

13 public function delete(int $id);14 }

InMemoryPersistence.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\Repository;4

5 use OutOfBoundsException;6

7 class InMemoryPersistence implements Persistence8 {9 private array $data = [];

10 private int $lastId = 0;11

12 public function generateId(): int13 {14 $this->lastId++;15

16 return $this->lastId;17 }18

19 public function persist(array $data)20 {21 $this->data[$this->lastId] = $data;22 }23

24 public function retrieve(int $id): array(continué en la próxima página)

1.4. More 129

Page 134: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

25 {26 if (!isset($this->data[$id])) {27 throw new OutOfBoundsException(sprintf('No data found for ID %d', $id));28 }29

30 return $this->data[$id];31 }32

33 public function delete(int $id)34 {35 if (!isset($this->data[$id])) {36 throw new OutOfBoundsException(sprintf('No data found for ID %d', $id));37 }38

39 unset($this->data[$id]);40 }41 }

Test

Tests/PostRepositoryTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\Repository\Tests;4

5 use OutOfBoundsException;6 use DesignPatterns\More\Repository\Domain\PostId;7 use DesignPatterns\More\Repository\Domain\PostStatus;8 use DesignPatterns\More\Repository\InMemoryPersistence;9 use DesignPatterns\More\Repository\Domain\Post;

10 use DesignPatterns\More\Repository\PostRepository;11 use PHPUnit\Framework\TestCase;12

13 class PostRepositoryTest extends TestCase14 {15 private PostRepository $repository;16

17 protected function setUp(): void18 {19 $this->repository = new PostRepository(new InMemoryPersistence());20 }21

22 public function testCanGenerateId()23 {24 $this->assertEquals(1, $this->repository->generateId()->toInt());25 }26

27 public function testThrowsExceptionWhenTryingToFindPostWhichDoesNotExist()28 {29 $this->expectException(OutOfBoundsException::class);30 $this->expectExceptionMessage('Post with id 42 does not exist');31

32 $this->repository->findById(PostId::fromInt(42));33 }

(continué en la próxima página)

130 Capítulo 1. Patterns

Page 135: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

34

35 public function testCanPersistPostDraft()36 {37 $postId = $this->repository->generateId();38 $post = Post::draft($postId, 'Repository Pattern', 'Design Patterns PHP');39 $this->repository->save($post);40

41 $this->repository->findById($postId);42

43 $this->assertEquals($postId, $this->repository->findById($postId)->getId());44 $this->assertEquals(PostStatus::STATE_DRAFT, $post->getStatus()->toString());45 }46 }

1.4.3 Entity-Attribute-Value (EAV)

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 givenentity is relatively modest.

1.4. More 131

Page 136: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

UML Diagram

Code

You can also find this code on GitHub

Entity.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\EAV;4

5 use SplObjectStorage;6

7 class Entity8 {9 /**

10 * @var SplObjectStorage<Value,Value>11 */12 private $values;13

14 /**15 * @var string

(continué en la próxima página)

132 Capítulo 1. Patterns

Page 137: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

16 */17 private string $name;18

19 /**20 * @param string $name21 * @param Value[] $values22 */23 public function __construct(string $name, $values)24 {25 /** @var SplObjectStorage<Value,Value> values */26 $this->values = new SplObjectStorage();27 $this->name = $name;28

29 foreach ($values as $value) {30 $this->values->attach($value);31 }32 }33

34 public function __toString(): string35 {36 $text = [$this->name];37

38 foreach ($this->values as $value) {39 $text[] = (string) $value;40 }41

42 return join(', ', $text);43 }44 }

Attribute.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\EAV;4

5 use SplObjectStorage;6

7 class Attribute8 {9 private SplObjectStorage $values;

10 private string $name;11

12 public function __construct(string $name)13 {14 $this->values = new SplObjectStorage();15 $this->name = $name;16 }17

18 public function addValue(Value $value)19 {20 $this->values->attach($value);21 }22

23 public function getValues(): SplObjectStorage24 {25 return $this->values;

(continué en la próxima página)

1.4. More 133

Page 138: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

26 }27

28 public function __toString(): string29 {30 return $this->name;31 }32 }

Value.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\EAV;4

5 class Value6 {7 private Attribute $attribute;8 private string $name;9

10 public function __construct(Attribute $attribute, string $name)11 {12 $this->name = $name;13 $this->attribute = $attribute;14

15 $attribute->addValue($this);16 }17

18 public function __toString(): string19 {20 return sprintf('%s: %s', (string) $this->attribute, $this->name);21 }22 }

Test

Tests/EAVTest.php

1 <?php declare(strict_types=1);2

3 namespace DesignPatterns\More\EAV\Tests;4

5 use DesignPatterns\More\EAV\Attribute;6 use DesignPatterns\More\EAV\Entity;7 use DesignPatterns\More\EAV\Value;8 use PHPUnit\Framework\TestCase;9

10 class EAVTest extends TestCase11 {12 public function testCanAddAttributeToEntity()13 {14 $colorAttribute = new Attribute('color');15 $colorSilver = new Value($colorAttribute, 'silver');16 $colorBlack = new Value($colorAttribute, 'black');17

18 $memoryAttribute = new Attribute('memory');

(continué en la próxima página)

134 Capítulo 1. Patterns

Page 139: DesignPatternsPHP Documentation...DesignPatternsPHP Documentation, Versión 1.0 Esto es un recopilatorio de los conocidos como ‘patrones de diseño‘_ junto con algunos ejemplos

DesignPatternsPHP Documentation, Versión 1.0

(proviene de la página anterior)

19 $memory8Gb = new Value($memoryAttribute, '8GB');20

21 $entity = new Entity('MacBook Pro', [$colorSilver, $colorBlack, $memory8Gb]);22

23 $this->assertEquals('MacBook Pro, color: silver, color: black, memory: 8GB',→˓(string) $entity);

24 }25 }

1.4. More 135