cult3

What is the Decorator Pattern?

Apr 23, 2014

Table of contents:

  1. What is the Decorator Pattern?
  2. Why would you use the Decorator Pattern?
  3. Extending the Decorator Pattern further
  4. When would you use the Decorator Pattern?
  5. Conclusion

If you read through the source code of popular applications it can seem a bit confusing as to why exactly code has been written or structure in a certain way. There are many well used Design Patterns in computer programming that allow us to solve problems. Once you start to recognise these patterns, you begin to be able to intuitively understand exactly what the developer was aiming to achieve with her code.

This not only makes solving problems quicker, but it also makes picking up someone else’s code a whole lot easier.

In this post I want to take a deep dive on the Decorator Pattern as it doesn’t seem to get much love by developers. The Decorator Pattern actually seems a bit weird when you first encounter if (well it did for me anyway), but in fact it has many benefits that you could probably use in your code today.

So let’s take a look at the Decorator Pattern!

What is the Decorator Pattern?

The Decorator Pattern is a method for changing the functionality of a class without changing the structure of the original class. You can think of it as inheritance but without actually inheriting anything.

So for example, say we had a File entity class in our application. We will want to get the path of the file so we can define an interface contract like this:

interface FileInterface
{
    /**
     * Get Path
     *
     * @return string
     */
    public function getPath();
}

The actual File class looks like this:

class File implements FileInterface
{
    /** @var string */
    protected $file;

    /** @var string */
    protected $url;

    /**
     * Construct
     */
    public function __construct($file, $url)
    {
        $this->file = $file;
        $this->url = $url;
    }

    /**
     * Get path
     */
    public function getPath()
    {
        return $this->file;
    }

    /**
     * Get URL
     */
    public function getUrl()
    {
        return $this->url;
    }
}

This class stores two bits of data about each file on the system, a file path and a url path. By default when we request the getPath() method we are returned the $file path.

However how would we get the file path under different circumstances? For instance if you were requesting the file through a browser, through a secure connection or through SSH?

Well we can use Decorators for each of these instances.

First we can create an Abstract Decorator class to extend each individual Decorator child class. Remember an Abstract is just like a boiler plate class that we use to create other classes:

class AbstractFileDecorator
{
    /** @var File */
    protected $file;

    /**
     * Construct
     */
    public function __construct(File $file)
    {
        $this->file = $file->file;
    }

    /**
     * Get path
     */
    public function getPath()
    {
        return $this->file->getPath();
    }
}

Next we can create individual Decorator classes for each situation where we will need access to the file:

class FileSystemDecorator extends AbstractFileDecorator implements FileInterface
{
    /**
     * Get Path
     *
     * @return string
     */
    public function getPath()
    {
        return $this->file->getPath();
    }
}

The FileSystemDecorator is basically just the same as the original class.

class UrlFileDecorator extends AbstractFileDecorator implements FileInterface
{
    /**
     * Get Path
     *
     * @return string
     */
    public function getPath()
    {
        return "https://example.com" . $this->url;
    }
}

When requesting the file through a browser we can simply return the url property rather than the file path.

class SecureUrlFileDecorator extends AbstractFileDecorator implements
    FileInterface
{
    /**
     * Get Path
     *
     * @return string
     */
    public function getPath()
    {
        return "https://example.com" . $this->url;
    }
}

For the SecureUrlFileDecorator we can simply prepend the file url with the secure https protocol.

class SshFileDecorator extends AbstractFileDecorator implements FileInterface
{
    /**
     * Get Path
     *
     * @return string
     */
    public function getPath()
    {
        return "user@example.com:" . $this->file;
    }
}

And finally, for the SshFileDecorator we can prepend the user’s SSH details. Obviously you wouldn’t hard code base urls or server details into the class, these are just examples.

Why would you use the Decorator Pattern?

So why would you use the Decorator Pattern? Well as you can see above, the Decorator Pattern allows you to modify the functionality of a class without actually changing the original class.

So you could create a new File like this:

$file = new File("/var/www/my-file.jpg", "/images/my-file.jpg");

And then you can use it in different circumstances like this:

// On a server
$server = new FileSystemDecorator($file);
$server->getPath(); // /var/www/my-file.jpg

// In a browser
$browser = new UrlFileDecorator($file);
$browser->getPath(); // https://example.com/images/my-file.jpg

// Through HTTPS
$https = new SecureUrlFileDecorator($file);
$https->getPath(); // https://example.com/images/my-file.jpg

// Through SSH
$ssh = new SshFileDecorator($file);
$ssh->getPath(); // 'user@example.com:'/var/www/my-file.jpg

Extending the Decorator Pattern further

Now that we can “decorate” the File entity class at runtime, we can keep wrapping it in further decorators without effecting the origin object.

For example, you could create a CacheDecorator that would return the file from the cache rather than hitting the file system, or a LoggerDecorator that would record every time the file was accessed.

For each new decorator that you add, you simply have to wrap the object in a new layer of functionality. The original object as well as every layer above it does not need to know anything about any of the other layers.

When would you use the Decorator Pattern?

Hopefully my example in this tutorial is good enough to show what situations are good for using the Decorator Pattern. Basically the Decorator Pattern is good for modifying a class at runtime. So like in the example above, if you need to modify the output of a class depending on the environment, the Decorator Pattern allows you to do that in a clean and simple way.

Favour composition over inheritance is an important computer programming mantra to live by. This basically means you should write your code so that it is malleable rather than rigid.

By using the Decorator Pattern we can modify the File class without having to inherit all of it’s functionality. In my very simple example above this doesn’t seem like much of a problem, but in reality the File class would have a lot of stuff that we don’t care about if we only wanted to modify the getPath() method.

By using decorators instead of inheritance, we can create tiny classes that have a single objective, rather than monolithic objects that are hard to maintain or evolve. The decorator pattern allows us to easily swap out, add, or remove functionality without ever changing the original object or any individual instance of it.

Conclusion

Hopefully this has been a good explanation of the Decorator Pattern with a solid real world example of the pattern in action.

I think the Decorator Pattern is one of the common design patterns that often gets forgotten about. In the example above, it would be tempting to just make child classes that inherits and overrides the getPath() method. But instead of inheriting all the functionality that we don’t need, we can use a decorator to simply modify the bit we are interested in at runtime.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.