2.5. Decorator

2.5.1. Propósito

Para añadir dinámicamente nueva funcionalidad a instancias de clase.

2.5.2. Ejemplos

  • Capa del Servicio Web: Decoradores JSON y XML para un servicio REST (en este caso, solo se debe permitir uno de estos, por supuesto)

2.5.3. Diagrama UML

Alt Decorator UML Diagram

2.5.4. Código

Puedes encontrar el código en GitHub

Booking.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Decorator;
 6
 7interface Booking
 8{
 9    public function calculatePrice(): int;
10
11    public function getDescription(): string;
12}

BookingDecorator.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Decorator;
 6
 7abstract class BookingDecorator implements Booking
 8{
 9    public function __construct(protected Booking $booking)
10    {
11    }
12}

DoubleRoomBooking.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Decorator;
 6
 7class DoubleRoomBooking implements Booking
 8{
 9    public function calculatePrice(): int
10    {
11        return 40;
12    }
13
14    public function getDescription(): string
15    {
16        return 'double room';
17    }
18}

ExtraBed.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Decorator;
 6
 7class ExtraBed extends BookingDecorator
 8{
 9    private const PRICE = 30;
10
11    public function calculatePrice(): int
12    {
13        return $this->booking->calculatePrice() + self::PRICE;
14    }
15
16    public function getDescription(): string
17    {
18        return $this->booking->getDescription() . ' with extra bed';
19    }
20}

WiFi.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Decorator;
 6
 7class WiFi extends BookingDecorator
 8{
 9    private const PRICE = 2;
10
11    public function calculatePrice(): int
12    {
13        return $this->booking->calculatePrice() + self::PRICE;
14    }
15
16    public function getDescription(): string
17    {
18        return $this->booking->getDescription() . ' with wifi';
19    }
20}

2.5.5. Test

Tests/DecoratorTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Decorator\Tests;
 6
 7use DesignPatterns\Structural\Decorator\DoubleRoomBooking;
 8use DesignPatterns\Structural\Decorator\ExtraBed;
 9use DesignPatterns\Structural\Decorator\WiFi;
10use PHPUnit\Framework\TestCase;
11
12class DecoratorTest extends TestCase
13{
14    public function testCanCalculatePriceForBasicDoubleRoomBooking()
15    {
16        $booking = new DoubleRoomBooking();
17
18        $this->assertSame(40, $booking->calculatePrice());
19        $this->assertSame('double room', $booking->getDescription());
20    }
21
22    public function testCanCalculatePriceForDoubleRoomBookingWithWiFi()
23    {
24        $booking = new DoubleRoomBooking();
25        $booking = new WiFi($booking);
26
27        $this->assertSame(42, $booking->calculatePrice());
28        $this->assertSame('double room with wifi', $booking->getDescription());
29    }
30
31    public function testCanCalculatePriceForDoubleRoomBookingWithWiFiAndExtraBed()
32    {
33        $booking = new DoubleRoomBooking();
34        $booking = new WiFi($booking);
35        $booking = new ExtraBed($booking);
36
37        $this->assertSame(72, $booking->calculatePrice());
38        $this->assertSame('double room with wifi with extra bed', $booking->getDescription());
39    }
40}