The decorator pattern is used to extend the functionality of a certain object in a runtime. In a statically typed language you need to define decorator interface, then subclass from it and initialize the component (object) that you are decorating.
In Ruby you can pledge: “Yes I am a grownup” and skip the formal interface definition part.
In either case the payoff comes with the fact that you can combine “extensions” without writing out complex inheritance tree that accounts for all possible combinations. Therefore you end up using extension that you need when you need them.
Let’s check it out on Wikipedia coffee example.
|
If we want some latte, we can create a new decorator for the coffee:
|
We can now have some latte:
|
You only need to implement methods that are extending the base methods. For double coffee we need to implement cost, but the ingredients method is just delegating to the decorated class:
|
Instead of accounting for all cases with inheritance (upfront aka tedious):
You can plug it in decorator pattern that looks something like this:
This is small example but I hope that the full scale of combination explosions problem that would occur in bigger scenarios is visible.
Ruby has a cool solution for delegation blues (writing boilerplate code to wrap up unchanged delegated methods). On the example below you are arguably at lost for using forwardable, but the more methods you need to delegate better of you are with forwardable.
|
If you have really big number of methods and you don’t need to be too selective about what you are delegating then you can pull out the big guns and implement some method_missing magic.
To digress a bit I thought that method_missing is the best thing since sliced bread and also really original idea. That was until I’ve met doesNotUnderstand message in Smalltalk. Then I felt humbled for a while… Man they had it back than and still it was new to me even today.
To get back on the decorator. You can be explicit about what’s going on:
|
Punchline is that decorators enable you to chain the functionality that you need the way you need it without spelling it out in inheritance tree:
|
Ruby modules
You can create the same effect by using modules. It looks rather sexy:
|
In practice you would probably build some kind of factory to ease the pain of all this flexibility for the client:
|
One gotcha is that you need to watch out for you methods to be truly chainable. For example if we had:
|
We would not get the same cost for:
|
Methods wrapping
One more alternative is to wrap up methods in runtime. For example you can implement DoubleCoffee decoration like this:
|
Rails has a real nice variation of this tehnique with their alias_method_chain method.
Conclusion
I think this is one of those patterns that looks really good in theory. And as they say, “in theory, theory and practice are the same”. I would say that in practice you need to carefully judge wether the flexibility gained is worthed increased complexity for the client code. Nevertheless it is nice to know that Ruby is offering some refreshing ways to do it if you really need to.