Decorator in PHP [Design pattern with examples]

Coding (Php 7.x)

Dec 12, 2019

Design patterns help solve software problem, the Decorator Pattern will add features to your object a piece of cake
 
/img/blog/decorator-in-php-design-pattern-with-examples.jpg

In this article you are going to build a meal system, with me, following the most performant design pattern that will do in this case:

 

the Decorator pattern;

 

The decorator pattern is a software design pattern that allows developers to change and enhance the behavior of an object without changing the code of the action that the object needs to perform.

 

Sound complicated,

 

If I didn't lose you already let's make it simpler.

 

The decorator pattern will help you create objects that can have a variable amount of characteristics while keeping your application’s code scalable.

 

Alright, let’s step back for a second,

 

You are reading the series “Design pattern in PHP“, 

 

In the previous episode, I explained that, even though it depends on the developer perspective, generally there are 3 categories of design pattern. 

 

We have already seen the factory method pattern, it belongs to the category of creational patterns.

 

In this chapter, we are going to see an example of a structural design pattern

 

What does it mean?

 

 

Structural design patterns in a nutshell

 

As per the name a structural pattern describes how to assemble an entity (either an object or a class) into a bigger structure, an ecosystem or related entities.

 

The intent of these types of patterns is to do so thanks to a set of relationships among classes and interfaces that allows the system to keep the whole structure reliable and flexible.

 

If we are going to fast here and you wish to slow down a bit you can read about the basics of PHP.

 

 

The Decorator pattern

 

The following sentence is a definition of the pattern;

 

The decorator pattern attaches additional responsibility to an object dynamically, 

 

decorators provide a flexible alternative to sub-classing for extending functionalities

 

Let’s investigate this statement step by step to understand its meaning, 

 

Then will see a couple of real-world examples

 

Consider an object, 

 

it can be a beverage if you are building a web application for a cafeteria or it can be a car if your client is a mechanic.

 

The decorator pattern appends additional responsibility to these objects,

 

For example, a car is responsible for turning on and carrying us anywhere we want,

 

we can attach an additional responsibility to this car by adding a taxi licence holder to it, now the car is responsible to carry us and our customer around, 

 

same for a beverage, we have a glass of hot water, pretty tasteless by itself, we add some ground coffee bean to it and now we have enough energy to survive the morning.

 

The decorator pattern attaches additional responsibility to this object dynamically,

 

we can add these responsibilities at runtime, we do not have to hard code what these add-ons will be and we can let the user decide or even better the code itself.

 

decorators provide a flexible alternative to sub-classing for extending functionality,

 

decorators take advantage of both composition and inheritance.

 

We are going to use composition to share behaviour among classes and inheritance to make sure that the decorator behaves like the object we implemented.

 

The aim is to end up with several decorators of the same family but that we can use either one alongside another or as a standalone decorator.

 

One of the best ways I have seen this pattern represented and the way I believe is the easier to understand is imagining this structure as a matryoshka doll.

 

At the core of all the dolls, there is the object we want to use, 

 

In our case a brand-new car or the glass with water.

 

Then we wrap our core object with another object that is of the same type (we add another doll to the structure).

 

It adds functionality, 

 

A tea bag into our glass of hot water releases substances that make us feel better, a spoiler attached to the back of a car will make the car more stable at high speed.

 

A big advantage of this pattern is that we can wrap as many matryoshkas as we want,

 

following the example, we can have a glass of hot water, with ground coffee beans, then add brown sugar, then steamed milk and eventually add toppings of chocolate and cinnamon.

 

This action of continuing wrapping objects make them able to communicate with each other, thus get or edit information,

 

Another characteristic is that in this pattern we have 2 different types of relationship.

 

The is-a and the has-a.

 

If 2 objects are of the same type, this means that they are interchangeable, 

 

we can wrap the base object with add-ons then use the wrappers for our application.

 

 

What problem are we trying to solve?

 

Adding new functionality to our objects and not screwing up our application can be complicated,

 

In the past, this has been a difficult task, and this was subject of several experimentations.

 

In the following paragraph, we'll go over a few examples to find out how to solve this issue.

 

In this application, we are going to create a few special vehicles.

 

Suppose we are in Hollywood and we are creating an application that allows us to manage vehicles that have been protagonists in blockbuster movies.

 

Solution 1: relationship

 

 

As you can see from this schema above these are just normal car, depending on where you leave it would have been easy to spot at least one of these three cars.

 

Let’s add some more details to our Hollywood dealership then


 

 

 

If we were to get a Pontiac Firebird and we add all these features above we would have a first elementary version of KITT, 

 

For whoever did not understand the reference here, stop right now and go watch and episode of the Knight Rider!.

 

...

 

You back?

 

Let’s keep going

 

As you can see this is a well-structured code and it follows the single responsibility principle (SOLID).

 

but in this case, it is very likely that the classes we instantiate, only exist to have a method, 

 

Let me be clear, this can be great but in this case, can also entail that we can end up with a class explosion.

 

 

Solution 2: would be to use booleans

 

Another solution can be to use booleans to indicate whether an object has or has not certain characteristics.

 

In this case, our class with have getter and setter for each feature:

 

There are a lot of issues here,

 

Imagine you are creating an object of the class Vehicle and the constructor take a dozen of boolean as parameters, 

 

You and your colleagues need to remember exactly the order in which these parameters need to be written to not overlap with each other.

 

Another problem that we have is that booleans cause conditional statements, and these statements, although necessary sometimes, make the code in an application more difficult to manage and way less scalable.

 

Then we can add that in case we have to add a new feature on our Vehicle (maybe a special weapon) we must update the code inside our class, this is a clear violation of the open-close principle

 

The next issue has to do with the interface segregation principle,

 

let’s say we have a setSkiMode() method to our class, well you can not add ski to a Beetle because it is supposed to race not to skiing, yet you still have to use the parameter in the construct when you build the object, even if you always have to set this boolean with a false value.

 

It is a waste of code

 

Lastly but not less important, a code that is written this way just looks confusional and not easy to read.

 

 

Solution 3: The decorator pattern

 

Let’s now have a look at how we can improve our system using the decorator pattern.

 

Remember, the goal here is to add functionality to an object, in a scalable and manageable way

 

Let’s try it by using the decorator pattern

 

 

 

 

At a first glance, this may seem similar to the first example but the concept to understand here is that the class FeatureDecorator IS-A Vehicle because it needs to have the same behaviour (the fame method in this case) and HAS-A Vehicle because all the concrete decorators will return a Vehicle instance

 

 

 

The Vehicle now acts as a baseline in our application, it can live by itself if you want a car just out of the car dealer or you can have it with more options such as AC, heated car seats, anti-missile and special nanotech skins.

 

A brand new car, let’s say a 1959 Cadillac Miller-Meteor Futura Duplex event though beautiful is not recognizable to the eyes of peoples, this means that if we had an imaginary value, an integer returned from the fame() method on this class it will be a simple 1, but if you put a special siren this value can add up to another 1, then we add some adhesive and a proton pack in the hood thing start to change,

 

Well, I am sure you have seen this car before:

 

 

The initial value of 1 was a base number that our concrete class had, adding all these features to the original price will increase its value.

 

Note that a vehicle has a base level of fame and it will work by itself, at the same time a decorator has a value of fame as well but it cannot live in their own,

 

This value on the decorators exists to be added to the base object

 

The principle here is that you cannot have chocolate powder on top of your cappuccino if you do not have the cup with cappuccino first.

 

Also, note that using this method you can have a Volkswagen Beetle with a consciousness decorator and you can have a Pontiac Firebird with a 3D Object Printer and the two object are completely isolated from each other and do not violate any of the SOLID principles

 

 

The official UML

 

At the beginning of this post, I wrote that we are now going to create a meal application using the decorator pattern. 

 

I have done some abstract example so far so now it is time to see some PHP code in a real-world example.

 

What are we going to do?

 

We are creating a dynamic menu PHP application for our friend's business,

 

He just opened a little hot dog stand in New York and he needs some helps with the order, 

 

After analyzing the problem we find out that we need to assure that each client have a meal and the customer can decide with type of meal he wants, what type of bun to use, meat or chicken, and all the condiments to add on top of the dish.

 

 

The Meal class work just as a contract (it can be an abstract class or an interface) and as a getCost() method that returns a number, to make the example easier let’s say it return an integer.

 

Among the child functions, we have the different types of Meals a customer can have,

 

Does he want a typical New York hot dog?

 

A burger or maybe he will opt for a healthier choice going for a salad?

 

Each of these class are meals, thus they have the getCost() method in them,

 

Another class child of the Meal class is the MealDecorator,

 

a difficult concept to understand the first time that you see this design pattern may be that this and all the concrete class that are Meals' children ARE meals too. 

 

How to implement the pattern

 

What we need to make this application work is an abstract class.

 

It specifies that all the children classes need a getCost() method, some concrete instantiable classes and the decorators.

abstract class Meal 
{
    abstract public function getCost(): float;
}

class HotDog extends Meal
{
    private $cost = 2;
    public function getCost(): float
    {
        return $this->cost;
    }
}

class Burger extends Meal
{
    private $cost = 4;
    public function getCost(): float
    {
        return $this->cost;
    }

}

 

 

Here we have declared the abstract class Meal and two concrete classes that can be instantiated, HotDog and Burger with their respective base price.

 

Nothing fancy.

 

abstract class MealDecorator extends Meal
{
    protected $meal;
    public function __construct(Meal $meal)
    {
       $this->meal = $meal;
    }
} 

 

Now we have instantiated the decorator, it does not implement the getCost() method which means that it must be declared as an abstract class.

 

I am defining a constructor that requires an object of the class Meal (not a concrete class).

 

A good developer code to an interface, not an implementation.

 

This allows us to use all the different type of meal we have in our system.

class CheeseDecorator extends MealDecorator
{
    public function getCost(): float
    {
        return $this->meal->getCost() + 2;
    }
}

class MustardDecorator extends MealDecorator
{
    public function getCost(): float
    {
        return $this->meal->getCost() + 1;
     }
}

 

Each of the concrete decorators here extends the abstract decorator which means that all decorators have a reference to the Meal object we are using as a baseline.

 

When we call the getCost() method these classes invoke the method on the Meal accordingly.

$meal = new Burger();
echo $meal->getCost();
// This prints 4 the cost of a basic burger;

$meal = new MustardDecorator(new Burger());
echo $meal->getCost();
// This prints 5: Burger is 4 + Mustard is 1 = 5

 

Basically, that’s it!

 

We can now combine an object with all the combination of decorators (condiments) the customers wants.

 

 

Conclusion

 

Even though this pattern can seem complicated at the beginning is not.

 

It is quite easy to implement and is incredibly extensible.

 

We can add new decorators and type of meals that will very easily fit in our system at runtime.

 

In fact, as you have seen from the examples above you can have a burger with mustard without the necessity to create the MustardBurgerMeal object.

 

where you can go from here?

 

If this is the first episode of this you read, you can start the series with the Factory Method Pattern.

 

otherwise, you can start using this pattern into your code while implementing good practice in your PHP code as well.

 
 
If you like this content and you are hungry for some more join the Facebook's community in which we share info and news just like this one!

Other posts that might interest you

Coding (Php 7.x) Dec 5, 2019

Design Patterns in PHP [Factory Method Pattern]

See details
Coding (Php 7.x) Dec 21, 2019

Observer in PHP [Design pattern with examples]

See details
Get my free books' review to improve your skill now!
I don't want to improve