2.5. Decorator
2.5.1. Purpose
To dynamically add new functionality to class instances.
2.5.2. Examples
- Zend Framework: decorators for
Zend_Form_Element
instances - Web Service Layer: Decorators JSON and XML for a REST service (inthis 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
- <?php
- namespace DesignPatterns\Structural\Decorator;
- interface Booking
- {
- public function calculatePrice(): int;
- public function getDescription(): string;
- }
BookingDecorator.php
- <?php
- namespace DesignPatterns\Structural\Decorator;
- abstract class BookingDecorator implements Booking
- {
- /**
- * @var Booking
- */
- protected $booking;
- public function __construct(Booking $booking)
- {
- $this->booking = $booking;
- }
- }
DoubleRoomBooking.php
- <?php
- namespace DesignPatterns\Structural\Decorator;
- class DoubleRoomBooking implements Booking
- {
- public function calculatePrice(): int
- {
- return 40;
- }
- public function getDescription(): string
- {
- return 'double room';
- }
- }
ExtraBed.php
- <?php
- namespace DesignPatterns\Structural\Decorator;
- class ExtraBed extends BookingDecorator
- {
- private const PRICE = 30;
- public function calculatePrice(): int
- {
- return $this->booking->calculatePrice() + self::PRICE;
- }
- public function getDescription(): string
- {
- return $this->booking->getDescription() . ' with extra bed';
- }
- }
WiFi.php
- <?php
- namespace DesignPatterns\Structural\Decorator;
- class WiFi extends BookingDecorator
- {
- private const PRICE = 2;
- public function calculatePrice(): int
- {
- return $this->booking->calculatePrice() + self::PRICE;
- }
- public function getDescription(): string
- {
- return $this->booking->getDescription() . ' with wifi';
- }
- }
2.5.5. Test
Tests/DecoratorTest.php
- <?php
- namespace DesignPatterns\Structural\Decorator\Tests;
- use DesignPatterns\Structural\Decorator\DoubleRoomBooking;
- use DesignPatterns\Structural\Decorator\ExtraBed;
- use DesignPatterns\Structural\Decorator\WiFi;
- use PHPUnit\Framework\TestCase;
- class DecoratorTest extends TestCase
- {
- public function testCanCalculatePriceForBasicDoubleRoomBooking()
- {
- $booking = new DoubleRoomBooking();
- $this->assertEquals(40, $booking->calculatePrice());
- $this->assertEquals('double room', $booking->getDescription());
- }
- public function testCanCalculatePriceForDoubleRoomBookingWithWiFi()
- {
- $booking = new DoubleRoomBooking();
- $booking = new WiFi($booking);
- $this->assertEquals(42, $booking->calculatePrice());
- $this->assertEquals('double room with wifi', $booking->getDescription());
- }
- public function testCanCalculatePriceForDoubleRoomBookingWithWiFiAndExtraBed()
- {
- $booking = new DoubleRoomBooking();
- $booking = new WiFi($booking);
- $booking = new ExtraBed($booking);
- $this->assertEquals(72, $booking->calculatePrice());
- $this->assertEquals('double room with wifi with extra bed', $booking->getDescription());
- }
- }