The Decorator Pattern is a highly used Object Oriented structural design pattern, that gives the ability to attach additional responsibilities to an object dynamically, without sub-classing.
When this pattern is used correctly, it can be extremely powerful due to the flexibility it offers.
So… Grab yourself a cup of coffee, or better yet… Some of THIS and let’s get to work! 😀
Now you’ve probably seen the Decorator Pattern implemented multiple times in many Object Oriented languages, so to change it up a bit, we’ll compare the implementation in both Classical inheritance and Psuedo-Classical inheritance with some Prototype inheritance functionality.
**NOTE: The JavaScript implementation of the decorator pattern is not pure Prototypal inheritance.
It’s referred to as “Pseudo-Classical” inheritance with a few aspects of Prototypal inheritance. I explain why once we get to the JavaScript implementation.
Before we start, let’s talk about why you would even consider using the Decorator pattern, by giving a practical example.
Suppose we’re designing a system for a restaurant…
Now, in our restaurant’s system, we have a drink application that contains a subsystem which calculates the price of different drinks that this restaurant sells.
However, since we don’t live in a perfect world, we assume that not every customer will want to order a drink exactly the way it’s specified on the menu.
Since declining custom drink orders is bad for business, and the number of possible drink combinations are high, we are going to have to think about our design thoroughly.
As you can probably already see, a typical inheritance hierarchy isn’t going to work by itself…
Sub-classing for every current and future drink type *combination* would become a maintenance nightmare, therefore another approach must be taken.
**NOTE: Before we start, I also want to make a point that this design is not what you would do for any real application, much less a production application.
Why?
Well for one, we wouldn’t just throw all of our concrete subclass “decorations” (Red Bull, Orange Juice, Monster, etc) all into a single hierarchy under the Decorator.
Clearly Red Bull and Orange Juice have nothing in common…
Well, besides being a liquid… But with good Object Oriented Analysis and Design, along with being a production restaurant application, that is clearly not enough.
Well… Now that I think about it…
That’s assuming that this application will *actually* need to be maintained in the future.
If it didn’t, we could just throw everything in one gigantic function…
Maybe main()?
lol.. I kid.. I kid… 😀
Why is it not enough?
Well, what would happen if we wanted to decorate our drink with fruit?
maybe a cherry?
how about a lime?
or what about a napkin?
maybe a straw?
As you can see, those objects aren’t anywhere near related to each other.
The only thing a cherry and a napkin have in common, is that some drinks may use both of them.. lol 😀
So these issues (along with many others) are the reason our Object Oriented design for the entire restaurant system needs to be thoroughly thought out.
The reason I have it implemented like this, is to keep it simple..
By doing so, we can focus all of our attention on the actual Decorator Pattern, and what it is doing, without confusion.
So, without further ado….
*Clears throat… Mic check 1…2…3…* 😀
—- Introducing the Decorator Pattern —-
The Decorator Pattern implementation in Java (Classical Inheritance)
Drink.java – Client Code
public class Drink { // Client Code public static void main(String[] args) { // create a drink with brand of vodka Vodka drink = new GreyGoose(); // display vodka brand and price System.out.println(drink.getBrand() + " $" + drink.calculatePrice()); // decorate the drink with a mixer // mix the drink with a large Red Bull Vodka mixedDrink = new RedBull(drink, "Large"); // decorate the drink with another mixer // mix the drink with a medium Orange Juice mixedDrink = new OrangeJuice(mixedDrink, "Medium"); // decorate the drink with yet another mixer // mix the drink with a Medium Monster mixedDrink = new Monster(mixedDrink, "Medium"); // Display the type and price of the drink's // mixed ingredients, along with the drink total, // and format output to 2 places System.out.printf("Mixing a drink of " + mixedDrink.getBrand() + "\nTotal price is: $%.2f", mixedDrink.calculatePrice()); } }
Vodka.java
public abstract class Vodka { protected String brand = "Unknown Vodka"; protected String getBrand() { return this.brand; } protected abstract double calculatePrice(); }
GreyGoose.java
public class GreyGoose extends Vodka public GreyGoose() { this.brand = "Grey Goose"; } public double calculatePrice() { return 32.99; } }
VodkaDecorator.java
public abstract class VodkaDecorator extends Vodka { protected Vodka drink; protected abstract double sizePrice(String size); protected abstract String getBrand(); protected abstract double calculatePrice(); }
RedBull.java
public class RedBull extends VodkaDecorator { private String size; private double price; public RedBull(Vodka drink, String size) { this.drink = drink; this.size = size; this.price = this.sizePrice(size); } protected double sizePrice(String size) { double price; switch (size) { case "Small": price = 2.99; break; case "Medium": price = 4.99; break; case "Large": price = 6.99; break; default: // unknown size - super wings price = 8.99; } return price; } public String getBrand() { return this.drink.getBrand() + "\nand a " + this.size + " Red Bull which gives you wings for $" + this.price; } public double calculatePrice() { return this.price + this.drink.calculatePrice(); } }
OrangeJuice.java
public class OrangeJuice extends VodkaDecorator { private String size; private double price; public OrangeJuice(Vodka drink, String size) { this.drink = drink; this.size = size; this.price = this.sizePrice(size); } protected double sizePrice(String size) { double price; switch (size) { case "Small": price = .99; break; case "Medium": price = 1.99; break; case "Large": price = 2.99; break; default: // unknown size - super orange price = 3.99; } return price; } public String getBrand() { return this.drink.getBrand() + "\nand a " + this.size + " Monster energy for $" + this.price; } public double calculatePrice() { return this.price + this.drink.calculatePrice(); } }
Monster.java
public class Monster extends VodkaDecorator { private String size; private double price; public Monster(Vodka drink, String size) { this.drink = drink; this.size = size; this.price = this.sizePrice(size); } protected double sizePrice(String size) { double price; switch (size) { case "Small": price = 3.99; break; case "Medium": price = 5.99; break; case "Large": price = 7.99; break; default: // unknown size - super energy price = 9.99; } return price; } public String getBrand() { return this.drink.getBrand() + "\nand a " + this.size + " fruity Orange Juice for $" + this.price; } public double calculatePrice() { return this.price + this.drink.calculatePrice(); } }
OK, let’s talk about what’s going on…
First, starting in our client code (Drink.java), we create a new drink by instantiating the concrete subclass Grey Goose as a type Vodka.
Next, we “decorate” our drink of Grey Goose with some Red Bull…
We do this by instantiating a concrete subclass of one of our “decorations”, and then inject our drink object (Grey Goose) into our Red Bull object.
By decorating (wrapping) our drink object, this will allow us to polymorphically call calculatePrice() on both objects (when unwrapping), since both Vodka subclasses and VodkaDecorator subclasses are of type Vodka.
**NOTE: Notice how our VodkaDecorator not only inherits from our abstract base class Vodka, but it also composes a Vodka object.
If you haven’t already seen why, having our VodkaDecorator inherit from Vodka and compose Vodka is extremely important.
Our decorations (RedBull, Monster, etc.) need to have the same “interface” as the object they’re decorating.
With VodkaDecorator, we’re using inheritance in order to gain the same super-type as our drink objects, but we’re using composition in order to gain behavior.
Gaining behavior through composition is where our flexibility comes from. If we didn’t, and gained our behavior through inheritance alone, then any needed behavior would have to be determined at compile time only from what our Vodka base class can offer.
Since we’re using composition we can mix our decorations in any order at runtime!
And that’s really all there is to it.. We can use as many or as few decorations that we need for our drink.
We could pass our drink object to multiple RedBull objects, or one Red Bull, one Orange Juice, and one Monster (like the implementation does), or none at all..
OK, now let’s discuss the implementation in JavaScript…
The decorator pattern isn’t really relevant in JavaScript like it is in statically typed languages like Java.
Extending objects at runtime are common with dynamically typed languages, and this behavior is native in JavaScript.
Because it’s more or less built into JavaScript, the behavior that we’ve seen the decorator pattern provide isn’t really needed.
Furthermore, when JavaScript’s native behavior isn’t sufficient, another pattern called a Mixin is usually much better suited.
However, let’s still take a look at what it looks like in JavaScript…
**NOTE: This JavaScript implementation is referred to as pseudo-classical inheritance, not prototypal.
Even though JavaScript is a classless language, this implementation still simulates classes and treats the simulated classes like a class based heirarchy.
This is why this type of inheritance implementation is referred to as “pseudo-classical”.
In true prototypal inheritance we have no need for classes, whether real or simulated, so abstracting a classical inheritance hierarchy doesn’t make much sense.
The Decorator Pattern implementation in JavaScript
Run output in JSFiddle
Drink.js – Client Code
var main = (function() { var drink = new GreyGoose('Grey Goose'); alert(drink.getBrand() + " $" + drink.calculatePrice()); var mixedDrink = new RedBull(drink, "Large"); alert(mixedDrink.getBrand() + "\nTotal price is: $" + mixedDrink.calculatePrice()); })();
Vodka.js
var Vodka = function() { if (this.constructor === Vodka) { throw new Error("Abstract"); } }; Vodka.prototype.getBrand = function() { if (this.brand === undefined) { this.brand = "Undefined Vodka Brand"; } return this.brand; } Vodka.prototype.calculatePrice = function() { throw new Error("Abstract Method"); }
GreyGoose.js
var GreyGoose = function(brand) { Vodka.apply(this, arguments); this.brand = brand; }; GreyGoose.prototype = Object.create(Vodka.prototype); GreyGoose.prototype.constructor = GreyGoose; GreyGoose.prototype.calculatePrice = function() { return 32.99; };
VodkaDecorator.js
var VodkaDecorator = function() { if (this.constructor === VodkaDecorator) { throw new Error("Abstract"); } }; VodkaDecorator.prototype.sizePrice = function(size) { throw new Error("Abstract Method"); }; VodkaDecorator.prototype.getBrand = function() { throw new Error("Abstract Method"); }; VodkaDecorator.prototype.calculatePrice = function() { throw new Error("Abstract Method"); }
RedBull.js
var RedBull = function(drink, size) { this.drink = drink; this.size = size; this.price = this.sizePrice(size); } RedBull.prototype = Object.create(VodkaDecorator.prototype); RedBull.prototype.constructor = RedBull; RedBull.prototype.sizePrice = function(size) { return function() { var price; switch (size) { case "Small": price = 2.99; break; case "Medium": price = 4.99; break; case "Large": price = 6.99; break; default: price = 8.99; // unknown size - super wings break; } return price; }(); // private method by closure }; RedBull.prototype.getBrand = function() { return this.drink.getBrand() + "\nand a " + this.size + " Red Bull which gives you wings for $" + this.price; }; RedBull.prototype.calculatePrice = function() { return this.price + this.drink.calculatePrice(); };
With the exception of not decorating our drink with as many decorations, the JavaScript implementation is exactly the same as the Java implementation.
Within the confines of JavaScript we use the same inheritance hierarchy, and we add in some prototypal aspects to create the same decorator pattern behavior.
The explanation of prototypal functionality is out of the scope of this tutorial, but using this behavior (and some hacky throwing of errors) we can mimic classical inheritance in JavaScript.
In actuality, implementing the behavior of the decorator pattern in JavaScript (in a non psuedo-classical way) is much simpler.
So there you have it!
The decorator pattern offers powerful flexibility when used appropriately and it will prove itself to be a powerful tool in your Object Oriented toolbox.
Have fun! 😀