2.5. Decorator
2.5.1. Purpose
To dynamically add new functionality to class instances.
2.5.2. Examples
Web Service Layer: Decorators JSON and XML for a REST service (in this case, only one of these should be allowed of course)
2.5.3. UML Diagram
2.5.4. Code
You can also find this code on 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}