![Demonstrating Decorator Design Pattern In PHP](https://webmobtuts.com/wp-content/uploads/2019/10/Demonstrating-Decorator-Design-Pattern-In-PHP-800x400.jpg)
The decorator design pattern is one of the structural patterns used to add extra functionality to classes without modifying classes internal structure.
Sometimes we need a way to modify a class without modifying the class internal structure. Imagine that you work on some kind of framework and want to update a specific package class, now you can go through the package classes and add your own code but remember this is not right as you may break the framework because you must not play with the framework core packages.
Another solution you might consider is Inheritance, so you can inherit the class(s) you want to update but in the other hand Inheritance is not suitable in all situations as you might end up with a cycle of unwanted tree of parent child relationships.
The most elegant solution to this problem is the decorator pattern. the idea behind the decorator pattern is that you have an abstract class that acts as the decorator, this class must contain the methods that you want to add. Then you have to add the different concrete classes that will implement this class.
Decorator Components
- Main interface: This interface contains the main methods that you must extend.
- Concrete class: This is the main class the implements the main interface and contains the initial functionality.
- Abstract decorator: This is the most important class, this class must implement the main interface and also dependency inject the main interface in the constructor.
- Decorator classes: These classes must extend from the abstract decorator and provides the extra functionality to the main concrete class.
To understand the idea we will see this in action with an example. Let’s imagine that we have a notification Api, this api send notification through the database like this one:
/** * Main interface */ interface INotification { public function send($data); } /** * Concrete class */ class DBNotification implements INotification { public function send($data) { echo "Send notification to database: <strong>" . $data . "</strong><br/>"; } } $notification = new DBNotification(); $notification->send("Hello world");
In the above code there are a simple interface and a simple concrete class that implement the interface and handles the actual implementation. Note that i didn’t add any database specific code because i concentrate only on clarifying the decorator pattern.
The two components shown above represent the main interface and the main concrete class which we described previously in Decorator Components.
Now we need a way to add another functionality to the DBNotification class so it’s be able to send notifications to the email also. To do this we need to create the abstract decorator class:
/** * Abstract Decorator */ abstract class NotificationDecorator implements INotification { protected $notifier; public function __construct(INotification $notifier) { $this->notifier = $notifier; } public function send($data) { $this->notifier->send($data); } }
The abstract decorator class must implement the INotification interface and also the constructor must inject the INotification interface so that we can call the subsequent INotification::send() method using $this->notifier->send($data).
Now to create the EmailNotification class is a matter of extending NotificationDecorator class as illustrated below:
class EmailNotification extends NotificationDecorator { public function send($data) { echo "Send notification to email: <strong>" . $data . "</strong><br/>"; $this->notifier->send($data); } } $notification = new DBNotification(); $notification = new EmailNotification($notification); $notification->send("Hello world");
If you run the above example now you will see that it outputs something like this:
Send notification to email: Hello world Send notification to database: Hello world
Pretty well, the decorator works and now the two classes fire sequentially and executes it’s own logic without touching the main DBNotification class.
We can add more classes to add extra extra functionality so let’s add another class to send notifications to slack and push notifications as shown:
class SlackNotification extends NotificationDecorator { public function send($data) { echo "Send notification to slack: <strong>" . $data . "</strong><br/>"; $this->notifier->send($data); } } class PushNotification extends NotificationDecorator { public function send($data) { echo "Send push notification: <strong>" . $data . "</strong><br/>"; $this->notifier->send($data); } } $notification = new DBNotification(); $notification = new EmailNotification($notification); $notification = new SlackNotification($notification); $notification = new PushNotification($notification); $notification->send("Hello world");
Output
Send push notification: Hello world Send notification to slack: Hello world Send notification to email: Hello world Send notification to database: Hello world
As you see the decorator classes run as a chain of objects like Pointers so if debug the $notification instance using var_dump():
echo "<pre>"; var_dump($notification); echo "</pre>"; // output object(PushNotification)#4 (1) { ["notifier":protected]=> object(SlackNotification)#3 (1) { ["notifier":protected]=> object(EmailNotification)#2 (1) { ["notifier":protected]=> object(DBNotification)#1 (0) { } } } }
As you see here the PushNotification class points to SlackNotification which in turn points to EmailNotification until it ends with DBNotification, and this is the decorator every decorator class points to another decorator until it ends with the main class that provides the main functionality.