2.5. Decorador (Decorator)

2.5.1. Objetivo

Adicionar dinamicamente novas funcionalidades para as instâncias de uma classe.

2.5.2. Exemplos

  • Camada de serviço da web: Decoradores JSON e XML para um serviço REST (neste caso, apenas um destes deve estar permitido.

2.5.3. Diagrama UML

Alt Decorator UML Diagram

2.5.4. Código

Você também pode encontrar este código no 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. Teste

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}