I thought it was worth sharing what I learnt about the pros and cons of using these techniques to implement a decorator.
The five different decorator implementations are:
- A simple wrapper (wherein we investigate closures)
- Monkey patching
- Prototypal inheritance
- Proxy object (ES6)
- For want of a better word: “middleware”
I have put some notes about this post in an appendix so as not to disrupt the flow. So if interested in a) why I am using ES6 syntax or b) why I am NOT using classes or c) a list of the source files for you to play with, check towards the end of this post.
First, the simple component I want to decorate:
It’s a simple component with a
printValue(val) method which will log the value with a suffix at the end. The suffix can be set using the
We want to decorate the
printValue(val) method with a decorator to validate our input and to lower case the value (in order to show chaining of decorators.) We created the
setSuffix(val) method in order to flush out any complexities when decorating one method in a component that has other members.
It’s worth noting that all my examples here except the last one will work for decorating an isolated function instead of a member function and is a much simpler case.
How we are going to decorate this component
Below is a diagram of how we are going to decorate our component. We will wrap our original “printValue” method first in a “lower case” decorator, then in a “validate” decorator. When a consumer of our decorated component calls “printValue”, first we will validate their value, then lower case it, then print it out.
(Note, this diagram shows that we could have added behaviour to our wrapper after the original call is made “on the way out”, however in these examples we are not doing this.
First example: decorating using a simple wrapper (wherein we investigate closures)
What is the most naïve implementation of a decorator I can think of? Just wrap an object by returning a new object that does something, then calls the original.
Show me the code!
First I’ll show the code as a whole then we’ll go through it step by step:
So what does that do?
The component factory is still the same as shown above. However, we decorate it by wrapping the object creation in decorator factory methods:
The decorator factory takes the original object and returns a wrapper object that just passes the calls through to the original object except for the 1 method we want to decorate:
The decorated function does it’s stuff (lower casing the value) and passes the value to the inner function “wrapping” it, or “decorating” it.
We can then keep wrapping our object creation in decorator factory methods on and on waiting for the invocation which will call them like opening a Matryoshka doll.
So after the object creation and decoration is complete we run our test code:
and this outputs:
The first function executed will be from the outer most decorator. In this instance, that’s the validator. The first call is valid, so execution is passed into the second decorator “toLowerCase” which lower cases the value. It in turn calls the original function which logs the lower cased value with the suffix added on.
The second attempt fails validation which shows halting the chain of decorators so the value is never logged.
What’s with that second validator decorator and setTimeout anyway?!
(The waiter analogy is a great way of explaining asynchronous code)
Why am I complicating things with an asynchronous call using setTimeout?
This “validator” function is emulating going off to a database to check the validity of the value by using setTimeout and calling the inner function in the callback. This way we can test if our implementations are still going to work when dealing with asynchronous code.
How do we make use of closures?
We could have just stored the inner object on the new object in order to call it later. Why didn’t we do that? Because it would have made it public and that would have been weird. I would have confused the user of my object. Do I call
instance._original.setSuffix(). Far better to make the object a private member.
We can use closures to give us this private member access though.
The official bit: What are closures?
“A closure allows a function to access captured variables through the closure’s reference to them, even when the function is invoked outside the scope of those variables.” (me, slightly rewording a definition from wikipedia)
A simple example:
(Just in case it wasn’t obvious what the double brackets in
wow()() do: first it executes the “wow” function, which returns an anonymous function which is immediately executed because of the second brackets.)
This works fine though, because when the function that is capturing a locally scoped variable (in this instance “val”) is returned, a closure is created which allows access to this variable even though it has gone out of scope.
Back to our wrapper:
Look again at the decorator:
It returns an object with the function:
but this function is referencing the “inner” object which will go out of scope as soon as the decorator method is returned. However, because there is an inner function referencing this variable, the inner function “captures” this variable and once the function is returned a closure is created.
This means the lifetime of the variable referencing our inner function is extended in order for the nested function to be able to be call it a later time.
Our own function can use the “inner” object because of the closure but it isn’t exposed publicly. BOOM. Our private variable.
Pros and cons of this method
We introduce closures here but that has little to do with this wrapper method, in fact every technique we show here uses closures to hide variables that would be better off private.
Other than that this is a very simple implementation which has an obvious downside: we have to wrap every single method on the inner object, not just the one we are decorating. Like this:
That is ugly and a pain. Wouldn’t it be great if our decorators could just define the wrapper behaviour they desire and not have to worry about the rest? Happily there are a few techniques that will do just that.
Let’s look at monkey patching.
Second example: decorating using monkey patching
What is monkey patching?
“The dynamic modification of a class or module.”
Simply put in this context:
- (me, just then)
So what is decorating using monkey patching?
“I’m gonna replace your function with mine, then I’ll call you from within me - wrapping you in me.”
- (me again)
Show me HOW!
How do you do that you ask? Well, I’ll show you. First I’ll show the code as a whole then I’ll take you through it step by step:
So what does that do?
The component is still the same; the decorators are different, and the way they are called is different. Instead of passing an object into a factory method that returns a new object, our decorator method is just operating on the existing object:
This decorator method does it’s monkey patching by storing the original “printValue” method in a local variable:
and then overwrites the original function with it’s own copy, which lower-cases the value, then passes that value onto the stored “inner” function:
We set up our decorators the same way as before. We wrap the printValue() function in a lower-caser decorator. Then we wrap that in a validator decorator:
Note the use of closure’s here to do the storing of the chain of inner functions still. The real difference is that we are just swapping out 1 function in the existing object instead of returning a brand new object wrapper.
What are the pros and cons of the monkey patching method?
People HATE monkey patching. Often with good reason.
Why all the hate? Because when I call a library function I don’t expect that functionality to change just because I included some other chumps completely irrelevant library.
Unfortunately if that chump decided to monkey patch some native function or some shared dependencies I’m in for a nasty surprise.
Now it may not be so bad if I’m just monkey patching my own code, but it’s still a little funky and some people “JUST SAY NO” to the technique. Fair enough.
It does have a pro over the previous method though. Our decorator functions only had to operate on the method they wanted to wrap. The rest of the component was left untouched. This means our decorator functions only had to deal with 1 responsibility: wrapping a function with new behaviour.
So if you don’t care about monkey patching, your base object has other public methods that need to be maintained and you want to keep the code simple then this technique maybe for you.
Okay what about prototypal inheritance then?
Third example: Prototypal inheritance
What is prototype inheritance?
Most developers are used to classical inheritance from Java or C# where a class is based on another class. Quite simply prototype inheritance is all about objects instead of classes: “Objects inherit properties from other objects”. Simple as that.
You can tell 1 object to “base” itself off another object by setting it’s prototype. What this really means is: if someone asks to access one of my members, first we will look on my object, but if I don’t have it I will delegate the access to my prototype. On this will go, all the way up the prototype chain.
Show me the code:
This example is extremely similar to the first example where we construct a new object that reaches into the inner object. The first example had the down side of having to copy every member to the new wrapper object in order to pass it through though. Here we take advantage of something called delegation in prototypal inheritance. Our new object is created using the inner object as it’s prototype. This way I don’t have to define “setSuffix” on my new object as when it is asked for my prototype will be checked for the existence of this member as well.
This makes it a great choice for use as a decorator implementation. Onto more learnin’ though…
Fourth example: Proxies
Proxies are the new kid on the block, specified in ES6, and look like a promising way to do some interesting AOP style programming techniques. So let’s see if they can help create a decorator.
What are proxies?
“The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).” (from MDN)
Oooo neat, we can inject custom behaviour into property lookup and function invocation? Sounds powerful? Well it is.
Show me the code!
Once again I will show the whole code and then dissect and explain the important bits:
Firstly, what’s this?!
Well, I have been testing this code using node.js. And node’s support for proxies isn’t great yet.
Firstly if you want to make use of proxies you have to run node with a switch:
Even then, the Proxy object in node ain’t ES6 compliant at the time of writing this blog. However if you:
and require it as above, then you get a lovely up to date ES6 compliant Proxy object to use. You still have to use the switch above though. (I guess the npm module still uses the non-compliant proxy object underneath it all.)
Next you will notice the component factory is identical but the decorator methods look very different:
Proxies give an amazing amount of power and they are worth reading up about in the Proxy section of mdn.
Here, the decorator object takes the inner object and returns a Proxy of this object. We only handle 1 thing in our Proxy object: the property accessors. We do this by specifying custom behaviour for the “get” handler.
Here we test to see if the property is the function we want to decorate. If it is then we return our new decorator function (which in this case lower-cases the value and then passes that into the inner printValue function.)
If it doesn’t match the name we just return the inner member. The observant among you will notice that once again we are making use of closures here.
Pros and cons of the Proxy technique
They key thing here is that although we have had to do some extra work to setup our proxy object, no matter how many members our wrapped object has, our decorators won’t get any more complex.
So this technique has the 2 great attributes:
- it isn’t monkey patching and
- we don’t have to manually redefine every one of the inner objects members
It is probably overkill however, I cannot see what this approach gives us over prototype inheritance. Proxies were created for AOP style stuff rather than decorators.
Also, as we said earlier, support isn’t great yet. If you are in node, then as I showed you earlier you can polyfill it.
However if you are in the browser, there is still no support in any version of Internet Explorer. Chrome only got support in version 49. Unfortunately it is my understanding that it is very difficult, if not impossible to polyfill this feature well in the browser.
Possibly this polyfill works: but it warns of serious performance issues.
The Fifth technique: “Middleware”?
An excellent characteristic of the previous examples was that the original object didn’t even need to know it was being decorated. By wrapping it, monkey patching it, inheriting from it or proxying it we were able to extend it’s behaviour WITHOUT modifying it - Wow that’s the OCP of SOLID, yay :)
What if our base object knows from the outset of it’s own creation that one particular method will be decorated, and what if we want to enable more interaction between the base functionality and the decorators. What if we want to make our decorator code even more simple by pushing some of the decorator logic down to the base object?
Show me the code!
By now you know the drill: I’ll show you the whole code then break it down for you afterwards:
Notice the main difference? Our original object knows it will be decorated and provides a specific method for you to add your decorators. Here our component sets up the decorator chain itself, you just have to supply it the list of decorators with the method:
The “addDecorators” method then loops through each decorator passing in the inner function into it. It then take the last decorator method and assigns it to the public member. This way it has set up the decorator chain itself. Notice it is choosing to reverse the order of decoration to make the order in the array passed in more readable:
The decorator method itself can then be extremely simple. All it has to do is return the wrapper function that wraps the function passed in as a parameter:
Pros and cons of the “middleware” approach
We get more control over our decorators by setting up the chaining ourselves. We are making use of this here by changing the order in which we wrap the function using
reverse() on the decorators array.
Taking more control of setting up the decorator chain also leads to vastly simpler decorator methods.
So by setting up our object to allow decoration and do the grunt work of setting up the function chain we accomplish a few goals:
- Our decorator functions are simple
- It gives us more control over the decorator chaining
- It’s arguably simpler to setup our decorator list, we just pass an ordered array of decorators into a method instead of worrying about the construction mechanics of our particular decorator pattern implementation
- It’s still OCP - our base implementation allows us to decorate without further touching the original object
- and it ain’t monkey patching or relying on Proxies
This is the most complex of implementations. If you are setting up some heavy weight decoration that needs more management than simple wrapping then this is probably the implementation for you.
I have called this the “middleware” implementation because:
- I can’t think of anything better to call it and
- It is the method Dan Abramov uses in his redux middleware implementation
We looked at five different techniques for implementing the decorator pattern. We learned something along the way.
- We did a naive approach which led to us having to manually copy every member from our inner object to our wrapper object. But we learnt about using closures to hide variables as if they were private members.
- We solved the “copy every member” problem by monkey patching, but we introduced monkey patching which many “consider harmful”.
- We solved the “copy every member” problem using prototype inheritance. Everything seemed warmer in the world.
- We solved the problem again using the ES6 Proxy object. However, we learnt the Proxy object doesn’t have great support yet and although is immensely powerful, it’s power is probably out of place here. It doesn’t really give us anything that prototype inheritance gave us in this instance.
- We looked at a “middleware” type implementation where the base object sets up the decorator chain itself. This led to a powerful and flexible implementation with simple decorator methods.
What can we conclude from all this?
Which was the clear winner? When I set out to write this I expected each technique to have it’s upsides and downsides, its own justification for when it should be used. Actually in the end I think there are only 2 implementations worth using:
- The prototype inheritance one and
- The “middleware” one
I guess use the middleware one when you need more control of the decorator chain itself. Otherwise the prototype inheritance one seems to me the clear winner. It has all the advantages:
- No modification of the base object required
- No “copy every member to the new object”
- No monkey patching
- No poorly supported code
- Relatively simple decorator methods
I am using ES6 syntax in this article for a number of reasons:
- I have fallen in love with many things about ES6 but especially arrow functions and the const keyword
- It (mostly) works natively now in recent versions of node.js and is a simple babel transpilation step away from working in browsers.
- It makes it much more easy to write examples in articles like this with less noise getting in the way. (I realise the down side of this is that this is not true for readers that haven’t learnt the new ES6 syntax yet.)
- I have spent a while reading and writing React code recently and it is all in ES6 nowadays, so it’s time for all of us to jump on board!
The babel site itself is where I started learning some ES6 if you are looking for a starting place to get your head around it.
I am using ES6 syntax but not using the class keyword? There are 2 excellent reasons for that:
- The “wrapper” method
- The “monkey patching” method
- The “prototype inheritance” method
- The “proxy” method
- The “middleware” method