It Feels Great to Decorate!
An epic poem about the Decorator design pattern.
I write lots of code, and I often rely
On the Decorator Pattern to get by.
It's a design pattern straight from the Gang of Four,
And it's really quite simple. Let me tell you more.
Decorating objects can help you out.
You don't need to extend. It's a different route
To add on behavior or to make a change.
It's a SOLID approach that's not too strange.
And decorators can be used a lot;
With Iterator objects, or others you've got
That have an Interface declared.
Let's take a closer look, so you need not be scared.
The Iterators feature in PHP,
Is a good example for us to see.
An Iterator, you know, is a class that can be
Used in a foreach, yielding value and key.
There's a base interface, that describes the way
To work with the concrete iterators in play.
Implementing this base are two SPL types:
LimitIterator receives little hype.
This little gem, simply limits the count
Of the items yielded, when traversing about.
The other is the CallbackFilterIterator,
Which is yet another useful collaborator.
This one drops items that do not return true
When passed into the callback provided by you.
But what if we want both these features together?
To limit and filter our items. Now, whether
We really want to, we most certainly cannot
Make a class that extends both iterators we've got.
But fear not! For as I earlier alluded
Decorators solve many problems, this included.
We can decorate an object, to add new behavior.
So, we'll just do it twice to our iterator.
We'll wrap it once, to filter things out.
We'll wrap it again, to limit the count.
We can decorate as many times as we need
To the desired effect that will help us succeed.
There's no need to extend; we simply compose,
Injecting the object we want to enclose.
The decorator then applies it's logic
When we use it, just like the original object.
This is possible, because the interface is the same
As the decorated object implements by name.
The decorator then acts as a proxy of sorts,
Delegating the methods that the interface supports.
And it intercepts the ones it wants to augment.
For iterators, this is usually the method current().
Decorators can be applied in other use cases,
And caching is one I've seen in a few places.
Let's say, we have an object that makes a calculation.
The math is too hard, so I'll skip that explanation.
We have an interface for this calculator of sorts,
So it's usage is clear to its object cohorts.
There’s a calculate() method that takes one argument,
And returns the result, after some time is spent.
The result is the same, as long as you call
With the same argument; it won't change it all.
So, why do the work a second or third time?
That would be a punishable crime!
Instead we make an object with which to decorate,
And cache the result that we calculate.
That way subsequent calls will complete a lot faster,
And our use of the calculator won't be a disaster.
To make our new class, we code to its interface,
and we write a constructor with which to encase
The original object, which we'll use in our code.
But we'll look in the cache first to reduce our load.
Our new calculate(), will see if we know
The result for the argument in the current workflow.
If we've got it, then great, we'll return straight away.
If not, then to the decorated class we'll relay.
Then we'll take the result and store it for later,
So the next time we'll know how to better cater
To the caller using our calculator. (Whew!)
Let's give a big hand to the pattern: Decorator.