2.5. Dekorator (Decorator)

2.5.1. Przeznaczenie

Pozwala na dynamiczne dodawanie nowych funkcji do istniejących klas podczas działania programu.

2.5.2. Przykłady

  • Web Service Layer: Dekorator typu JSON i XML używany w usłudze typu REST (w tym przykładzie tylko jeden z nich powinien być dozwolony).

2.5.3. Diagram UML

Alt Decorator UML Diagram

2.5.4. Kod

Ten kod znajdziesz również na 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. Testy

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}