Decorator Pattern in PHP

When we want to add additional features to our existing classes we generally use decorator pattern.
Do you find this definition useful?
I believe the answer will be NO.
It says about only adding new features. It doesn’t say about how you will use that. Will you add the features by simply hard coding or you’ll add them in the run time.
That’s a pretty big difference.
Why?
Let us consider a simple example where we are talking about a hosting company. A typical hosting company sells hosting services and sometimes they also provide template designing.
Let’s design a pretty basic scenario.

// a Web service center

class HostingService {

public function Cost() {

return 20;
}
}

class TemplateDesign {

protected $hosting = 10;

public function Desisn() {
return $this->hosting + 15;
}
}

$CostofHosting = new HostingService();
//var_dump($CostofHosting->Cost());
$HostingAndDesign = new TemplateDesign();
var_dump($HostingAndDesign->Desisn());

It’s a very basic hosting application where if you want to get only hosting services it will cost 20 dollar. If you want to go for the both – hosting and template designing – then there is a discount. In such cases, the hosting services will be half.
Where is the flaw in this code?
It runs fine. When we run it for only hosting service it returns 20. And when we run it for hosting and designing, it returns 25.
The real problem lies in adding new features. When the template design part has been added with hosting services, we have to hard code the discount. Imagine a situation where you have decided to increase hosting cost. You need to hard code that cost into your hosting class first. Next you need to calculate and make the amount half in the combined hosting and design class.
This is a real problem. It cannot go on this way. But you need to add features.
How you can overcome this problem?
The decorator pattern is the answer where you can design your application in such a way that will allow you to add features in the run time.
Moreover, when you work in a group, your friends can also participate.
Consider this code snippet first.

// a Web service center

interface WebService {
public function getDescription();
public function getCost();
}

class HostingService implements WebService {

public function getDescription() {
return "Hosting services cost ";
}
public function getCost() {

return 2000;
}
}

class WebDesign implements WebService {

protected $websrvice;

public function __construct(WebService $webservice) {
$this->websrvice = $webservice;
}

public function getDescription() {
return "The web designing costs ";
}

public function getCost() {
return 1500 . "\n";
}

public function HostingAndDesigning() {
return 1500 + $this->websrvice->getCost() - $this->websrvice->getCost() * 20 / 100 . " . For "
. "hosting and designing jointly you"
. " save " . $this->websrvice->getCost() * 20 / 100 . "\n";
}
}
$CostofHosting = new HostingService();
$webdesign = new WebDesign($CostofHosting);
echo $webdesign->getDescription();
echo $webdesign->getCost();
echo $webdesign->getDescription() . " with hosting ";
echo $webdesign->HostingAndDesigning();

We have decided to use a contract first. What is contract? That will keep things hidden from the original classes. It will encapsulate our application.
Watch the first part of the code.

interface WebService {
public function getDescription();
public function getCost();
}

We want description and the cost of the web services that you’re going to provide.
For brevity we imagine your web services company provide hosting services and web design services. Let us first think about the hosting services.

class HostingService implements WebService {

public function getDescription() {
return "Hosting services cost ";
}
public function getCost() {

return 2000;
}
}

Very simple. Along with the description it also shows the cost – that returns 2000 dollar. (If you think it’s too much, please bear with it. It’s just an example.)
The third part is important where you’re supposed to add new features. For this case you have added web design services along with the hosting services. We’ll have getDescription() and getCost() methods. But you have also decided to inject another service into it – HostingAndDesigning(). If someone wants to buy both features – hosting and designing – she’ll get a 20 percent discount on the hosting services.

class WebDesign implements WebService {

protected $websrvice;

public function __construct(WebService $webservice) {
$this->websrvice = $webservice;
}

public function getDescription() {
return "The web designing costs ";
}

public function getCost() {
return 1500 . "\n";
}

public function HostingAndDesigning() {
return 1500 + $this->websrvice->getCost() - $this->websrvice->getCost() * 20 / 100 . " . For "
. "hosting and designing jointly you"
. " save " . $this->websrvice->getCost() * 20 / 100 . "\n";
}
}

Run the code, you’ll see this output:

Hosting services cost 2000

The web designing costs 1500

The web designing costs with hosting 3100. For hosting and designing jointly you save 400

You have used decorator pattern and added new features. Now suddenly you decide to change the hosting services cost and want to make it 200 instead of 2000. If such situation arises, you needn’t have to worry about hard coding the code in every class. Just change it in you’re your second part of the code:

class HostingService implements WebService {

public function getDescription() {
return "Hosting services cost ";
}
public function getCost() {

return 200 . "\n";
}
}

Now see the magic in your output.

Hosting services cost 200
The web designing costs 1500
The web designing costs with hosting 1660. For hosting and designing jointly you save 40

Everything changes in the run time due to using the decorator pattern. Even more importantly you can keep adding on new features in your application.
Are you ready to get started in adding new features? Shall we begin?
Let us imagine a situation where web designing services will be modified with few other services like wordpress designing and drupal designing. You are ready to give your client special web designing services where more options are available.
Now you can use abstract class. This abstract class will indicate that your application is segmented just in the web designing section. It doesn’t touch the hosting services segment. This abstract class is handling with the web design part only. Now the full code looks like this.

// a Web service center

interface WebService {
public function getDescription();
public function getCost();
}

class HostingService implements WebService {

public function getDescription() {
return "Hosting services cost ";
}
public function getCost() {

return 200 . "\n";
}
}

class WebDesign implements WebService {

protected $websrvice;

public function __construct(WebService $webservice) {
$this->websrvice = $webservice;
}

public function getDescription() {
return "The web designing costs ";
}

public function getCost() {
return 1500 . "\n";
}

public function HostingAndDesigning() {
return 1500 + $this->websrvice->getCost() - $this->websrvice->getCost() * 20 / 100 . " . For "
. "hosting and designing jointly you"
. " save " . $this->websrvice->getCost() * 20 / 100 . "\n";
}
}

abstract class WebDesignFeatures implements WebService {
protected $webdesign;
public function __construct(WebDesign $webdesign) {
$this->webdesign = $webdesign;
}
public function getDescription(){
$this->webdesign->getDescription();
}
public function getCost() {
$this->webdesign->getCost();
}
}

class WordPressDesign extends WebDesignFeatures {

protected $webdesign;

public function getDescription() {
return $this->webdesign->getDescription() . " with wordpress designing: ";
}

public function getCost() {
return $this->webdesign->getCost() + 2000 . "\n";
}
}

class DrupalDesign extends WebDesignFeatures {

protected $webdesign;

public function getDescription() {
return $this->webdesign->getDescription() . " with drupal designing: ";
}

public function getCost() {
return $this->webdesign->getCost() + 2500 . "\n";
}
}

$CostofHosting = new HostingService();
echo $CostofHosting->getDescription();
echo $CostofHosting->getCost();
$webdesign = new WebDesign($CostofHosting);
echo $webdesign->getDescription();
echo $webdesign->getCost();
echo $webdesign->getDescription() . " with hosting ";
echo $webdesign->HostingAndDesigning();

$wordpressdesign = new WebDesign($CostofHosting);
$templateandwordpress = new WordPressDesign($wordpressdesign);
echo $templateandwordpress->getDescription();
echo $templateandwordpress->getCost();

$drupaldesign = new WebDesign($CostofHosting);
$templateanddrupal = new DrupalDesign($drupaldesign);
echo $templateanddrupal->getDescription();
echo $templateanddrupal->getCost();

Let us see what our output says.

Hosting services cost 200
The web designing costs 1500
The web designing costs with hosting 1660. For hosting and designing jointly you save 40
The web designing costs with wordpress designing: 3500
The web designing costs with drupal designing: 4000

Now if you want you can add any feature anywhere. You can change any part and you don’t have to hard code it into your application. Suppose you want to add a discount only in the wordpress designing segment. The other part of the application is not affected. Our application follows the SOLID design principle and objects are loosely coupled.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s