Decorator : Design Pattern

Sep 22, 2020 By Puneet verma

Let's understand it

Example 1

We've got a Person class that holds a surname:

class Person {
    private $surname ;
    public function __construct($surname)
    {
        $this->surname = $surname;
    }

    public function displayAllWeKnow()
    {
       return "surname "$this->surname;
    }
}


$p =new Person("Verma");
echo $p->displayAllWeKnow();  // surname Verma


It works perfectly for surnames, but we need first names also as requirements grow.

Solution 1: The obvious solution is to add one more variable

class Person {
    private $surname ;
    private $firstname;
    ...

Sometimes, this is the right thing to do. But for particular complex required, it may have broader effects

  • We have changed the variable, so the constructor will also change. We have to reflect these changes in all places where we have that objects. In some places, it may not possible to accumulate these changes.
  • We have to test all the scenarios where the object is changed.
  • It breaks the Open close principle. Software should be open to extension but closed to modification. Well,

If we are ok with these effects, then we can go with this solution

Solution 2: Subclass

class Person {
    private $surname ;
    public function __construct($surname)
    {
        $this->surname = $surname;
    }

    public function displayAllWeKnow()
    {
        return $this->surname;
    }
}

class PersonWithName extends Person {
    private $firstName ;
    private $ps;

    public function __construct($ps, $firstName)
    {
        $this->ps = $ps;
        $this->firstName = $firstName;
    }


    public function displayAllWeKnow()
    {
        return "First Name ".$this->firstName. " surname ". $this->ps->displayAllWeKnow();
    }
}

$ps = new Person("Verma");

$pf = new PersonWithName($ps,"Puneet");
echo $pf->displayAllWeKnow();   //First Name Puneet surname Verma

 

  • Our original Person class is entirely unchanged.
  • We can use the child class where the first name is required without affecting the places where it's not needed.
  • Doing things this way minimises the codebase change and the required re-testing effort.
  • Liskov Substitution Principle is compatible.

Again new requirement came. We need a middle name, also.
We can apply the same logic and have one more subclass inherited from PersonWithName. That will work. What if we require different combinations

  • first name + middle name
  • first name + surname
  • surname + middle name
  • first name + middle name +surname

For each combination, we can have a subclass for the required behaviour.

What if we have n number of required? Then will have a complex mesh of subclass, and impractical to do so.

 

Let's have one more example to understand this. 

We have an application which sends notifications for important events.

It starts with e-mail notifications and then extends to SMS and slack notifications. Up to this, everything works smoothly.

decorator subclass

 

At some point, you realize that users require several notification types at once, like SMS, Facebook or any possible combination.

You tried to address that problem by creating special subclasses which combined several notification methods within one class. However, it quickly became apparent that this approach would bloat the code immensely, not only the library code but also the client code.

 

subclassing  problem

So subclassing is not very useful, for such a problem.

Solution 3: Decorators

We have a special design for such situations.

<?php

interface Person
{
   public function displayAllWeKnow();
}

class Surname implements Person
{
    private $surname;
    public function __construct()
    {
        $this->surname = "Verma";
    }
    public function displayAllWeKnow()
    {
        return $this->surname;
    }
}

class FirstName implements Person {

    private $person;
    private $firstName;

    public function __construct($person) // in php $person as type in not required, in some language we have declare like Person $person
    {
        $this->person = $person;
        $this->firstName = "Puneet";

    }

    public function displayAllWeKnow()
    {
        return $this->person->displayAllWeKnow()." ".$this->firstName;
    }
}


class MiddleName implements Person {

    private $person;
    private $middleName;

    public function __construct($person) // in php $person as type in not required, in some language we have declare like Person $person
    {
        $this->person = $person;
        $this->middleName = "Kumar";

    }

    public function displayAllWeKnow()
    {
        return $this->person->displayAllWeKnow()." ".$this->middleName;
    }
}


$surname = new Surname();
$surnameFirstName = new FirstName($surname);

$surnameMiddleName = new MiddleName($surname);

$SurnameFirstMiddle = new MiddleName($surnameFirstName);

$SurnameMiddleFirst = new FirstName($surnameMiddleName);


echo $surname->displayAllWeKnow();             //Verma
echo $surnameFirstName->displayAllWeKnow();    //Verma Puneet
echo $surnameMiddleName->displayAllWeKnow();   //Verma Kumar
echo $SurnameFirstMiddle->displayAllWeKnow();  //Verma Puneet Kumar
echo $SurnameMiddleFirst->displayAllWeKnow();  //Verma Kumar Puneet

Note Ordering affects the output.

echo $SurnameFirstMiddle->displayAllWeKnow();  //Verma Puneet Kumar
echo $SurnameMiddleFirst->displayAllWeKnow();  //Verma Kumar Puneet

 

Decorator

  • Attach additional responsibilities to an object dynamically.
  • Decorators provide a flexible alternative to subclassing for extending functionality.
  • It's a structural design pattern.
  • Modify the functionality of an object at runtime,  without affecting individual objects.
  • The decorator pattern is similar to Wrapper. A wrapper is an object that can be linked with some target object. The wrapper contains the same set of methods as the target and delegates to it all requests it receives. However, the wrapper may alter the result by doing something before or after passing the request to the target.

Real-world Analogy

  • When we buy a car, we often buy its seat covers, stereo system, sensors etc. So we are decorating the car with add-on features.
  • Wearing clothes is an example of using decorators. When you’re cold, you wrap yourself in a sweater. If you’re still cold with a sweater, you can wear a jacket on top. If it’s raining, you can put on a raincoat. All of these garments “extend” your basic behaviour but aren’t part of you, and you can easily take off any piece of clothing whenever you don’t need it.

Pros

  • You can extend an object’s behaviour without making a new subclass.
  • You can add or remove responsibilities from an object at runtime.
  • You can combine several behaviours by wrapping an object into multiple decorators.
  • Single Responsibility Principle. You can divide a monolithic class that implements many possible variants of behaviour into several smaller classes.

Cons

  • It’s hard to remove a specific wrapper from the wrapper stack.
  • It’s hard to implement a decorator in such a way that its behaviour doesn’t depend on the order in the decorator's stack.
  • The initial configuration code of layers might look pretty ugly.

 

Let's explore one more variation of solution 3. you may observe the same constructor we declare multiple times. To avoid this, we can have an abstract class.


interface Person
{
   public function displayAllWeKnow();
}

class Surname implements Person
{
    private $surname;
    public function __construct()
    {
        $this->surname = "Verma";
    }
    public function displayAllWeKnow()
    {
        return $this->surname;
    }
}

abstract class Name implements Person{
    protected $person;
    public function __construct($person)  // in php $person as type in not required, in some language we have declare like Person $person
    {
        $this->person = $person;
    }
}

class FirstName extends Name {
    private $firstName ="Puneet";
     public function displayAllWeKnow()
    {
        return $this->person->displayAllWeKnow()." ".$this->firstName;
    }
}


class MiddleName extends Name {

    private $middleName = "Kumar";

    public function displayAllWeKnow()
    {
        return $this->person->displayAllWeKnow()." ".$this->middleName;
    }
}


$surname = new Surname();
$surnameFirstName = new FirstName($surname);

$surnameMiddleName = new MiddleName($surname);

$SurnameFirstMiddle = new MiddleName($surnameFirstName);

$SurnameMiddleFirst = new FirstName($surnameMiddleName);



echo $surname->displayAllWeKnow();             //Verma
echo $surnameFirstName->displayAllWeKnow();    //Verma Puneet
echo $surnameMiddleName->displayAllWeKnow();   //Verma Kumar
echo $SurnameFirstMiddle->displayAllWeKnow();  //Verma Puneet Kumar
echo $SurnameMiddleFirst->displayAllWeKnow();  //Verma Kumar Puneet

 

Example 2

interface  Car
{
    function cost();
    function description();
}

 class Suv implements Car{
     function cost()
     {
         return 30000;
     }

     function description()
     {
         return "Suv";
     }
 }

 abstract class CarFeature implements Car{

     protected $car;
     public function __construct(Car $car)
     {
         $this->car = $car;
     }
 }

 class SunRoof extends CarFeature{

     function cost()
     {
         return $this->car->cost() +1500;
     }

     function description()
     {
         return $this->car->description().", sunroof";
     }
 }


class HighEndWheels extends CarFeature{


    function cost()
    {
        return $this->car->cost() + 2000;
    }

    function description()
    {
        return $this->car->description().", high end wheels";
    }
}

$basicCar = new Suv();

echo $basicCar->description(); //Suv
echo "\n"; 

$carWithSunRoof = new SunRoof($basicCar);
echo $carWithSunRoof->description(); //Suv, sunroof
echo "\n";

$carWithHighEndWheels = new HighEndWheels($basicCar);
echo $carWithHighEndWheels -> description(); //Suv, high end wheels
echo "\n";

$carWithSunRoofAndHighEndWheels = new HighEndWheels($carWithSunRoof);
echo $carWithSunRoofAndHighEndWheels -> description(); //Suv, sunroof, high end wheels
echo "\n";


$carWithHighEndWheelsAndSunRoof = new SunRoof($carWithHighEndWheels);
echo $carWithHighEndWheelsAndSunRoof -> description();  //Suv, high end wheels, sunroof
echo "\n";

 

Example 3:

<?php

interface FoodItem
{
    public function cost();
}


// all we need is a burger
class Burger implements FoodItem
{
    public function cost () 
    {
      return 4;
    }
}

// Now decorate with cheese
class Cheese implements FoodItem
{
    private $item;

    public function __construct (FoodItem $item) 
    {
        $this->item = $item;
    }    

    public function cost () 
    {
        return $this->item->cost() + 0.25;
    }
}

// now decorate with patty
class Patty implements FoodItem 
{
    private $item;

    public function __construct (FoodItem $item) 
    {
        $this->item = $item;
    }    

    public function cost () 
    {
        return $this->item->cost() + 1;
    }
}

$b  = new Burger();
$c  = new Cheese($b); // passing burger
$pb = new Patty($b); // passing burger
$pc = new Patty($c); // passing cheese burger$b->cost(); // 4


$c->cost(); // 4.25
$pb->cost(); // 5
$pc->cost(); // 5.25

 

Implementation

  • We need a base class and above that will add features (decorate it)
  • All classes implement the component interface.

Usages

  • A few Java classes from the java.io package, like BufferedInputStream and LineNumberInputStream, use decorator design patterns.