Decorator design pattern in functional and object oriented programming
Agile Software Development relies on the ability to change fast. New user stories and enhancements to existing stories are going to happen. Therefore you code will change.
But what about S.O.L.I.D’s Open/Closed Principle ?
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
The OCP preaches that once an entity is done, it’s done. You should provide them a way to be enhanced/extended.
In Object Oriented Programming this might be achieved with Design Patterns (like Strategy, Template Method), Inheritance, or just good modeling.
In Funcional Programming, otherwise, you could accomplish that with composition, partial application, currying, higher order functions, among other concepts.
Today we are using a pattern supported by both paradigms called decorator.
Decorators are wrapper functions that enhance the wrapped function. You don’t change the original function behavior, instead you decorate it with new behavior, achieving extensability and composability.
You create a function to abstract the sometimes complicated spawn/exec Node API. These abstractions are called facades:
This is a promisified facade for the complicated NodeJS spawn API. It accepts two arguments, a shell command string to run, and some options to the spawn native function. It runs the command, prints to stdout, and resolves or reject the promise.
First feature request: print command being run
The users loved the product, but they would like to see what command was issued by them, not just its output.
You can just add a new
print.info(command) inside the first line.
But now the function name does not express what it does. It runs AND prints a command.
Second feature request: memoize last command
Users want to have the option to run the last command.
We create a function called
memoize(command) that stores the last run command using a persistence mechanism.
We could call memoize before every command call, but then we would add temporal coupling to the commands that need to be momoized, so let’s add this behavior to the
This is the result:
This function is doing too much, and getting bigger and bigger. We are going to use the decorator pattern, so we can compose these behaviors without modifying the original function.
Meet the decorator pattern
Decorators are wrapper functions that enhance the wrapped function. There is a TC39 proposal to turn it into an ECMA Script specification. If the proposal becomes a standard, we will have support to decorators as an ECMA Script first class language construct. But since currently it’s just in stage 2, which is the draft stage, we are going to create our own decorators, using Higher Order Functions.
First let’s change our
runCommand function to the original version.
Let’s decorate this function with the print behavior. First we create a higher order function that receives a function and prints the first argument. Then we return the decorated function itself:
withCommandLogger decorator returns a decorated function. We can use it seamlessly as we didn’t know it has been decorated. Notice that we didn’t modify the original
runCommand function, which is nice if you want to use it without printing the command being run.
Now let’s add the memoize feature to the command by decorating it again:
Third Feature Request: measure command elapsed time
Now that we know the decorator pattern, it’s easy to enhance the original function without modifying it. Let’s implement the third feature request: measure commands elapsed time. Let’s create the
We use the
process.hrtime Node API to record high resolution time ellapsed, avoiding some
Date API precision drawbacks. Note that this decorator actually executes the decorated function, so it should be the last one to be called.
Decorator is a design pattern that achieves high levels of extensibility, without modifying the original function. It provides us a way to enhance our functions dinamically, by composition.
More generic decorators with rest parameters
You might have noticed that our decorators are coupled with the original
runCommand function signature, which expects two parameters. We can make our decorators to enhance any function by using rest parameters:
By using the rest parameter sintax
... the decorators are not aware of the decorated function parameters. They just receive any number of arguments and pass them to the decorated function.
Object Oriented Decorators with C#
In OOP, classes are decorated through inheritance. Decorators extend the decorated class and override the decorated class method or property they want to enhance. The overridden method is enhanced and then calls the super class original method.
Imagine we are developing a first person shooter and we are modeling the weapon object. We should support attaching gadget to modify our weapon damage, bullet capacity and wheight. Our weapon can be rendered and should be able to shoot a target.
We will create an abstract weapon with those characteristics:
We created several fields and properties to describe our weapon, a shot method that deals damage to a target and render method, which is abstract because it’s up to each weapon to decide how it should look.
Let’s create a plasma pistol:
Now we want to add attachments to this weapon. Each attachment may change the weapon’s attributes, and how its rendered. We could use inheritance for that, but we would end up an overwhelmingly complex class hierarchy:
As you can see, it’s not possible to model these attachments with inheritance because we’d end up with a hierarchy mess.
Decorator is a great pattern for solving that.
In OOP, decorators are implemented by inheriting from the decorated class and by receiving a reference to the decorated object in the construcutor.
Let’s create a banana clip attachment to increase our weapon’s bullet capacity by 10 (and therefore its weight):
The decorator also changed how the weapon is rendered. To decorate a weapon with this attachment, we just need to pass the decorated weapon as the decorator constructor argument:
Both vanilla Plasma Pistol and the decorated one are instances of Weapon, so our client code don’t care about the types. Because of that, we can decorate an already decorated weapon, since the decorators inherits from Weapon, just like the Plasma Pistol. Let’s create some other attachments to see that:
Our HollowPoint decorator increases the weapon demage, and our SilencerDecorator renders a fancy James Bond silencer. We can know compose our weapons with attachments, just like Battlefield!
Decorator is a design pattern for dinamically enhancing objects without modifying the original behavior by wrapping, enhancing, and calling the wrappee. What do you think about it, have you found it usefull, or just too pragmatic?