Decorator pattern dynamically attaches additional responsibility to an object. This pattern is used to extend functionality without affecting the existing implementation.
If we just use inheritance, then we can extend the functionality of a class. But by using a decorator we can enhance/extend and/or change the functionality of an object at run time.
This article demonstrates Decorator pattern implementation in PHP. Check the implementation below.
Implementation
Here are the steps for Decorator pattern implementation-
- Create an interface for the subject/item classes.
- Create subject/item classes and implement the interface.
- Create an abstract class as decorator class and implement the same subject interface. Define a property of type of the subject interface and initialize it in the constructor. In the method implementation call the methods from the subject.
- Create decorator class and extend the abstract decorator. In the method implementation call the method from the parent(which in turn uses the methods from the subject). Then add additional functionality in the method implementation.
Here is a simple example of Decorator pattern implementation in PHP-
<?php
// PHP Decorator Pattern
// Subject
interface Subject {
function operationOne(): void;
function operationTwo(): void;
}
// Concrete subject
class ConcreteSubject implements Subject {
public function operationOne(): void {
echo "Performing Operation One(1) in Subjectn";
}
public function operationTwo(): void {
echo "Performing Operation Two(2) in Subjectn";
}
}
// Decorator
abstract class Decorator implements Subject {
public function __construct(protected Subject $subject) {
}
public function operationOne(): void {
$this->subject->operationOne();
}
public function operationTwo(): void {
$this->subject->operationTwo();
}
}
// Concrete decorator
class ConcreteDecorator extends Decorator {
public function __construct(Subject $subject) {
parent::__construct($subject);
}
public function operationOne(): void {
$this->subject->operationOne();
echo "Performing additional operation in Concrete Decoratorn";
}
}
// Demo
$someDecorator = new ConcreteDecorator(new ConcreteSubject());
$someDecorator->operationOne();
Output of the above code will be as below-
Performing Operation One(1) in Subject
Performing additional operation in Concrete Decorator
Examples
Here are some examples of Decorator pattern.
Example #1: Data Export
In this example we have a simple class for data export. We want to add some additional functionality to that.
So, we are creating some decorators for CSV, JSON, and Excel. These will wrap the existing exporter implementation and will add some extra functionality on top of that.
DataExport Interface [Existing]
- Create file “DataExport.php”.
- Define interface “DataExport”.
- Declare method “processData”. There can be other method declaration as per the requirement.
<?php
// DataExport.php
namespace BigBoxCodeDesignPatternDecoratorDataExport;
interface DataExport {
function processData(): void;
}
SimpleExport Class [Existing]
- Create file “SimpleDataExport.php”.
- Define class “SimpleDataExport”.
- Implement “DataExport” interface for the class.
<?php
// SimpleDataExport.php
namespace BigBoxCodeDesignPatternDecoratorDataExport;
class SimpleDataExport implements DataExport {
public function processData(): void {
echo "Processing Datan";
}
}
Main Decorator Class [Decorator]
- Create file “DataExportDecorator.php”.
- Define class “DataExportDecorator”.
- Implement “DataExport” interface for the class.
- Define a property “$dataExport”. Accept param in the constructor and set the value.
- In the “processData” method implementation call the “processData” method from “$dataExport”.
<?php
// DataExportDecorator.php
namespace BigBoxCodeDesignPatternDecoratorDataExport;
class DataExportDecorator implements DataExport {
public function __construct(protected DataExport $dataExporter) {
}
public function processData(): void {
$this->dataExporter->processData();
}
}
CSV Decorator Class [Decorator]
- Create file “CsvDataExportDecorator.php”.
- Create class “CsvDataExportDecorator”.
- Extend the “DataExportDecorator” for the class.
- In the constructor call the parent constructor and set the “$dataExport” value.
- In the “processData” method implementation call the “processData” from the parent “$dataExport”. Call additional processing steps in the same method for extra operations. Here we have created a separated method named “processCsv” and called it from “processData”.
<?php
// CsvDataExportDecorator.php
namespace BigBoxCodeDesignPatternDecoratorDataExport;
class CsvDataExportDecorator extends DataExportDecorator {
public function __construct(DataExport $dataExporter) {
parent::__construct($dataExporter);
}
public function processData(): void {
$this->dataExporter->processData();
$this->processCsv();
}
public function processCsv(): void {
echo "Processed data to CSVn";
}
}
Excel Decorator Class [Decorator]
- Create file “ExcelDataExportDecorator.php”.
- Create class “ExcelDataExportDecorator” and extend “DataExportDecorator”.
- In the “processData” method implementation call the “processData” from “$dataExport” and also call additional processing steps for excel processing.
<?php
// ExcelDataExportDecorator.php
namespace BigBoxCodeDesignPatternDecoratorDataExport;
class ExcelDataExportDecorator extends DataExportDecorator {
public function __construct(DataExport $dataExporter) {
parent::__construct($dataExporter);
}
public function processData(): void {
$this->dataExporter->processData();
$this->processExcel();
}
public function processExcel() {
echo "Processed data to Exceln";
}
}
JSON Decorator Class [Decorator]
- Create file “JsonDataExportDecorator.php”.
- Create class “JsonDataExportDecorator” and extend “DataExportDecorator” for the class. In the “processData” implementation add additional steps for JSON data processing.
<?php
// JsonDataExportDecorator.php
namespace BigBoxCodeDesignPatternDecoratorDataExport;
class JsonDataExportDecorator extends DataExportDecorator {
public function __construct(DataExport $dataExporter) {
parent::__construct($dataExporter);
}
public function processData(): void {
$this->dataExporter->processData();
$this->processJson();
}
public function processJson(): void {
echo "Processed data to JSONn";
}
}
Demo
To use any specific exporter pass an object of “SimpleDataExport” to the decorator.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternDecoratorDataExportCsvDataExportDecorator;
use BigBoxCodeDesignPatternDecoratorDataExportExcelDataExportDecorator;
use BigBoxCodeDesignPatternDecoratorDataExportJsonDataExportDecorator;
use BigBoxCodeDesignPatternDecoratorDataExportSimpleDataExport;
$csvDataExport = new CsvDataExportDecorator(new SimpleDataExport());
$csvDataExport->processData();
$excelDataExport = new ExcelDataExportDecorator(new SimpleDataExport());
$excelDataExport->processData();
$jsonDataExport = new JsonDataExportDecorator(new SimpleDataExport());
$jsonDataExport->processData();
Output
The output of the demo above will be like below.
Processing Data
Processed data to CSV
Processing Data
Processed data to Excel
Processing Data
Processed data to JSON
Example #2: UI Elements
In this example we have some UI elements, like – Button, Input Box, and Table. We want to implement some decorators, like – margin, border, and background.
UI Element Interface
- Create file “UiElement.php”.
- Create interface “UIElement”.
- Declare methods as per requirement. Here we have method – “draw”.
<?php
// UiElement.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
interface UIElement {
function draw(): void;
}
Button UI Element Class
- Create file “Button.php”.
- Create class “Button”.
- Implement “UIElement” interface for the class. Define the “draw” method as part of the implementation.
<?php
// Button.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
class Button implements UIElement {
public function draw(): void {
echo "Drawing Buttonn";
}
}
Input Box UI Element Class
- Create file “InputBox.php”.
- Create class “InputBox” and implement “UIElement” interface for the class.
<?php
// InputBox.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
class InputBox implements UIElement {
public function draw(): void {
echo "Drawing Input Boxn";
}
}
Table UI Element Class
- Create file “Table.php”.
- Create class “Table”. Implement “UIElement” for the interface.
<?php
// Table.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
class Table implements UIElement {
public function draw(): void {
echo "Drawing Tablen";
}
}
Abstract UI Decorator [Decorator]
- Create file “UiDecorator.php”.
- Create class “UIDecorator”.
- Implement “UIElement” interface for the class.
- Define a protected and readony property of type “UIElement” in the class. Here we have named it “$uiElement”. This will be used to store a reference to a UI element class object.
- In the constructor accept param and set “$uiElement”.
- In the “draw” method implementation call the “draw” method from “$uiElement”.
<?php
// UiDecorator.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
abstract class UIDecorator implements UIElement {
protected function __construct(protected readonly UIElement $uiElement) {
}
public function draw(): void {
$this->uiElement->draw();
}
}
Border Decorator Class [Decorator]
- Create file “BorderDecorator.php”.
- Create class “BorderDecorator”.
- Extend the abstract decorator “UIDecorator”.
- In the constructor accept “$uiElement” and pass that to the parent constructor.
- In the “draw” method implementation call the “draw” from the parent(which calls the “draw” method from $uiElement”). Then add additional functionality as per the new requirement.
<?php
// BorderDecorator.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
class BorderDecorator extends UIDecorator {
public function __construct(UIElement $uiElement) {
parent::__construct($uiElement);
}
public function draw(): void {
parent::draw();
echo "Adding Border to the elementn";
}
}
Background Decorator Class [Decorator]
- Create file “BackgroundDecorator.php”.
- Create class “BackgroundDecorator” and extend the “UIDecorator”.
<?php
// BackgroundDecorator.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
class BackgroundDecorator extends UIDecorator {
public function __construct(UIElement $uiElement) {
parent::__construct($uiElement);
}
public function draw(): void {
parent::draw();
echo "Adding Background to the elementn";
}
}
Margin Decorator Class [Decorator]
- Create file “MarginDecorator.php”.
- Define class “BackgroundDecorator” and extend the “UIDecorator”.
<?php
// MarginDecorator.php
namespace BigBoxCodeDesignPatternDecoratorUiElement;
class MarginDecorator extends UIDecorator {
public function __construct(UIElement $uiElement) {
parent::__construct($uiElement);
}
public function draw(): void {
parent::draw();
echo "Adding margin to the elementn";
}
}
Demo
In the client, we can pass the UI element object to any decorator to implement the decorator for the element.
Multiple decorators can be used for the same element by chaining the call of the decorators.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternDecoratorUiElementBackgroundDecorator;
use BigBoxCodeDesignPatternDecoratorUiElementBorderDecorator;
use BigBoxCodeDesignPatternDecoratorUiElementButton;
use BigBoxCodeDesignPatternDecoratorUiElementInputBox;
use BigBoxCodeDesignPatternDecoratorUiElementMarginDecorator;
use BigBoxCodeDesignPatternDecoratorUiElementTable;
$tableWithBorder = new BorderDecorator(new Table());
$tableWithBorder->draw();
$inputWithBorderAndBackground = new BackgroundDecorator(new BorderDecorator(new InputBox()));
$inputWithBorderAndBackground->draw();
$buttonWithAllDecorator = new MarginDecorator(new BackgroundDecorator(new BorderDecorator(new Button())));
$buttonWithAllDecorator->draw();
Output
Output of the demo code will be as below-
Drawing Table
Adding Border to the element
Drawing Input Box
Adding Border to the element
Adding Background to the element
Drawing Button
Adding Border to the element
Adding Background to the element
Adding margin to the element
Source Code
Use the following link to get the source code:
Other Code Implementations
Use the following links to check Decorator pattern implementation in other programming languages.