2.5. Декоратор (Decorator)

2.5.1. Назначение

Динамически добавляет новую функциональность в экземпляры классов.

2.5.2. Примеры

  • Web Service Layer: Декораторы JSON и XML для REST сервисов (в этом случае, конечно, только один из них может быть разрешен).

2.5.3. Диаграмма UML

Alt Decorator UML Diagram

2.5.4. Код

Вы можете найти этот код на 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. Тест

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}