The Decorator pattern is useful when you want to add some behavior to an object (or compose combinations of behavior) at run time but without adding that same behavior to an entire class of objects.
I think this capability can be very useful especially when only parts of an application might need that change and not all of it.
Let’s see some examples…
Lets take the Coffee Shop example
The example I see often used on many websites is where a coffee shop offers coffee and a number of extras like milk, sugar, spices, etc. that could be added to a cup of coffee.
- To avoid class explosion (making a class for every possible coffee combination), the coffee shop implements the Decorator pattern to compose whatever permutation of coffee a customer might ask for.
- They treat each of those parts as a “Beverage” (implementing a Beverage interface). To me that makes no sense, because sugar and spices aren’t a beverage.
- Anyway, they start with coffee object. Then inject that into sugar object. Then inject that into a milk object. And so on. Until the stack of objects unwinds and ads up a final price and description. To me this example isn’t very practical. Yes you could compose objects at run time any way you want but here it seems overkill. Instead why not just have an array of objects passed to a coffee object, then iterate through and add up their prices and descriptions vs use the Decorator pattern.
A more practical Decorator example
I think what’s more practical is extending an object (at run time) with a wrapper class, like this:
<?php interface Logger { function log(); } class SimpleLogger implements Logger { function log() { print "The info to log...\n"; } } class VerboseLogger implements Logger { private $logger; function __construct(Logger $logger = null) { $this->logger = $logger; } function log() { print "Adding some additional info before...\n"; if(isset($this->logger)) $this->logger->log(); print "And adding additional info after\n"; } } $simplelogger = new SimpleLogger(); $verboselogger = new VerboseLogger($simplelogger); $verboselogger->log();
And the output is
Adding some additonal info before... The info to log... And adding additional info after
In the above example a SimpleLogger object is created and then injected into a VerboseLogger object. The latter wraps the former and adds some additional logging info (before and after) without adjusting the SimpleLogger class in any way.
I think this example is more practical than the coffee shop example because it could be that the SimpleLogger is already used many places within an application. And you may only want to extend a subset of those objects with VerboseLogger. Because you only need additional info logged at certain points.
Deprecating old code
Another example I’ve heard of (very similar to the above example) is to wrap old code that needs to be deprecated in some places and not in others. Let’s say we create a version two of our SimpleLogger class called “SimpleLoggerV2”. We might only want parts of the system to be adjusted rather than the whole thing to reduce risk of things breaking. By wrapping the old code with a decorator the old behavior can be adjusted or even ignored.
The Decorator Pattern is cool!
Whatever the case the Decorator pattern is nice and gives us flexibility to add behavior to existing objects at run time. And helps when we need to adjust behavior across part of a system vs all of it.