2.5. Decorator

2.5.1. Rôle

Ajoute dynamiquement de nouvelles fonctionnalités aux instances de classe.

2.5.2. Exemples

  • Couche de service web : Décorateurs JSON et XML pour un service REST (dans ce cas, un seul d’entre eux doit être autorisé bien sûr)

2.5.3. Diagramme UML

Alt Decorator UML Diagram

2.5.4. Code

Vous pouvez également trouver ce code sur 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}