Earlier in this series we explored both the facade and adapter design patterns in this series. Using facade, we can simplify large systems, and by implementing adapter we can stay safe while working with external API and classes. Now we are going to cover the decorator design pattern, which also falls under the category of structural patterns.
We can use the decorator pattern when we just want to give some added responsibility to our base class. This design pattern is a great alternative to a sub‑classing feature for extending functionality with some added advantages.
The Problem
If you are confused and think that we can achieve the same functionality with a sub‑classing feature as well, then let me show you some code examples which will remove your confusion and make you love the decorator pattern.
I am going to take an example of a class which is responsible for generating content for an email. In the next code block, as you can see, this class functions very well for generating email content without any modification.
class eMailBody { private $header = 'This is email header'; private $footer = 'This is email Footer'; public $body = ''; public function loadBody() { $this->body .= "This is Main Email body.<br />"; } }
We know that Christmas is coming, and let's say I want to greet my reader with a message for my next newsletter email. So I have to add a message in my email body with an image which looks good.
For this I can directly edit in my email class, which I really don't want to do. So I can implement inheritance to achieve the same effect. I create a separate child class of the main email body class:
class christmasEmail extends eMailBody { public function loadBody() { parent::loadBody(); $this->body .= "Added Content for Xmas<br />"; } } $christmasEmail = new christmasEmail(); $christmasEmail->loadBody(); echo $christmasEmail->body;
So I am done with my code, and after a few days I want to send an email with New Year's greetings. We can use the same method as we did for Christmas.
class newYearEmail extends eMailBody { public function loadBody() { parent::loadBody(); $this->body .= "Added Content for New Year<br />"; } } $newYearEmail = new newYearEmail(); $newYearEmail->loadBody(); echo $newYearEmail->body;
This went smoothly without any more issues. Now let's say I forget to greet my visitors for both occasions (Christmas and New Year) and I want to send both greetings in one email, without modifying any code in the base class.
Your mind gets filled immediately with the following question: Will sub-classes and inheritance help here? I would be in favor of going that way, but we will need to use extra/unnecessary code to achieve this. We can use traits which enable us to implement something similar to multiple inheritance.
The Solution
The problem which we have discussed in the previous section can be resolved by implementing the decorator pattern.
According to Wikipedia:
The decorator pattern (also known as Wrapper, an alternative naming shared with the Adapter pattern) is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.
In the above section we have seen that we can extend features/behavior using one sub-class, but when it comes to adding multiple features/behaviors, it becomes lengthy and complex. And that's where we should use the decorator pattern.
Interface
interface eMailBody { public function loadBody(); }
This is a simple interface to make sure that some class must implement the required methods.
Main Email Class
class eMail implements eMailBody { public function loadBody() { echo "This is Main Email body.<br />"; } }
This is the main class which is generating the default body of an email, which I generally use to send emails. What I need, however, is to modify body content based on some occasion but without altering the main email class.
Main Decorator
abstract class emailBodyDecorator implements eMailBody { protected $emailBody; public function __construct(eMailBody $emailBody) { $this->emailBody = $emailBody; } abstract public function loadBody(); }
This is our main decorator class, which holds the reference to our main email class and changes its behavior as needed. Here we have defined one abstract method, loadBody
, which sub decorator needs to implement to change behavior.
Sub Decorator
class christmasEmailBody extends emailBodyDecorator { public function loadBody() { echo 'This is Extra Content for Christmas<br />'; $this->emailBody->loadBody(); } } class newYearEmailBody extends emailBodyDecorator { public function loadBody() { echo 'This is Extra Content for New Year.<br />'; $this->emailBody->loadBody(); } }
Here we have created two sub-classes of the main decorator, which actually performs a change of behavior for our main email class.
Wrapping It All Together
We have created all required elements. All we need to do is use our code and enjoy.
interface eMailBody { public function loadBody(); } class eMail implements eMailBody { public function loadBody() { echo "This is Main Email body.<br />"; } } abstract class emailBodyDecorator implements eMailBody { protected $emailBody; public function __construct(eMailBody $emailBody) { $this->emailBody = $emailBody; } abstract public function loadBody(); } class christmasEmailBody extends emailBodyDecorator { public function loadBody() { echo 'This is Extra Content for Christmas<br />'; $this->emailBody->loadBody(); } } class newYearEmailBody extends emailBodyDecorator { public function loadBody() { echo 'This is Extra Content for New Year.<br />'; $this->emailBody->loadBody(); } }
Now we will use this decorator class in various ways as needed:
/* * Normal Email */ $email = new eMail(); $email->loadBody(); // Output This is Main Email body. /* * Email with Xmas Greetings */ $email = new eMail(); $email = new christmasEmailBody($email); $email->loadBody(); // Output This is Extra Content for Christmas This is Main Email body. /* * Email with New Year Greetings */ $email = new eMail(); $email = new newYearEmailBody($email); $email->loadBody(); // Output This is Extra Content for New Year. This is Main Email body. /* * Email with Xmas and New Year Greetings */ $email = new eMail(); $email = new christmasEmailBody($email); $email = new newYearEmailBody($email); $email->loadBody(); // Output This is Extra Content for New Year. This is Extra Content for Christmas This is Main Email body.
We can see that now we have altered the body of an email without modifying the main email class.
Conclusion
Every application we have needs some sort of alteration and/or improvements at uniform intervals. So in such a case we can implement the decorator design pattern and it will ultimately improve code quality and make our code more extendable.
This was my effort to explain you about the decorator pattern, but if you have additional comments or questions, please don't hesitate to add them in the feed below.
Comments