DEV Community

Cover image for Decorator Pattern vs. Proxy Pattern
Doeke Norg
Doeke Norg

Posted on • Edited on • Originally published at doeken.org

Decorator Pattern vs. Proxy Pattern

There are two patterns in PHP that are very similar; The Decorator Pattern and The Proxy Pattern. Because they are so similar, you can quickly mistake one for the other. Does that matter? Maybe not, but I think it's good to know the differences when communicating about them.

Similarities between Decorators and Proxies

Both the Decorator Pattern and the Proxy Pattern revolve around the idea of wrapping an instance of an existing interface (let's call that the inner instance) with a class that implements that same interface and delegates their function calls to the same functions on their inner instance.

These patterns are very useful for adding or changing functionality of an instance without breaking encapsulation. It can also change or extend functionalities of final functions and classes. And because they usually serve one purpose they can easily be tested.

Example

interface SubscriberInterface {
    public function subscribe(string $email): void;
}

class SubscriberDecorator implements SubscriberInterface {
    private SubscriberInterface $inner_subscriber;

    public function subscribe(string $email): void {
        $this->inner_subscriber->subscribe($email);
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example you can see that our SubscriberDecorator implements the SubscriberInterface and it also requires some instance of the SubscriberInterface. After that it delegates the subcribe() function to the same function on that instance.

Differences between Decorators and Proxies

When it comes to naming a class a Decorator or a Proxy you have to look at its intent. What is the class actually doing with the instance it is wrapping?

Required vs. Optional dependency

You might have noticed I didn't include a __construct() method in the previous example. This was intentional, because this is where the first difference can be apparent.

A Decorator requires an instance of the interface it is wrapping, while a Proxy does not require such an instance. A Proxy can receive an instance, but is also allowed to create this instance itself. So you can create a new Proxy on its own, while a Decorator needs another instance as dependency.

// Decorator
 public function __construct(public SubscriberInterface $inner_subscriber){}

// Proxy
 public function __construct(?SubscriberInterface $inner_subscriber = null){
    $this->inner_subscriber = $inner_subscriber ?? new InnerSubscriber();
}
Enter fullscreen mode Exit fullscreen mode

Additive vs. Restrictive

Decorators are additive; meaning they only add new functionality by wrapping the function call and returning the original value. It can however do anything before or after that call. You can for example log every value when a function is called or dispatch an event. Just make sure to return the original value.

Proxies are restrictive; meaning they can change the behavior of a function or even restrict calling a specific function by throwing an exception.

Tip: Both Decorators and Proxies are allowed to add any extra functions or parameters. It can therefore be wise to implement some magic __isset(), __get() and __call() methods on the Decorator or Proxy to pass these calls along to the inner instance as well.

General purpose vs. Specific purpose

Decorators serve a general purpose. It will add some functionality regardless of the instance it is wrapping. This means that multiple decorators should be able to be applied on top of one another in any random order and still produce the same result and added functionality.

Proxies serve a more specific purpose. It will mostly be used to change or append functionality to a specific instance of the interface. Proxies also aren't commonly stacked on top of one another as a single proxy is usually enough.

Tips for Decorators and Proxies

Here are a few tips you might consider when working with Decorators and Proxies.

Make a base abstraction

If you create multiple Decorators or Proxies of the same interface it can be beneficial to create an abstract class of the interface or a trait that satisfies the interface, where every function is already deferred to the function on the inner instance. If you are a package creator, you might even consider providing this implementation inside the package. This way a Decorator or Proxy can extend or use this implementation and only (re)declare the functions it needs.

Single responsibility Decorators

It might be tempting to slap on multiple features onto a Decorator, but the beauty of them is that they can be added or removed without changing the underlying code. So try to make tiny Decorators that focus on one thing and apply these on top of each other. Again, this simpleness makes them easier to test as well.

Examples

You can find a couple of nice examples of Decorators and Proxies in Symfony.

Their developer toolbar shows a lot of information regarding events and cache, for example. They log this information by decorating the current EventDispatcher with a TraceableEventDispatcher and the current cache adapter with a TraceableAdapter within the dev environment.

An example of a Proxy can be found in the DeflateMarshaller of the symfony/cache package. This Marshaller is restrictive due to its dependency on gzinflate() & gzdeflate() and its changes to the output of the inner instance.

Thanks for reading

I hope you enjoyed reading this article! If so, please leave a ❤️ or a 🦄 and consider subscribing! I write posts on PHP almost every week. You can also follow me on twitter for more content and the occasional tip.

Top comments (0)