A JavaScript implementation of the Decorator pattern

July 17th, 2007. Tagged: JavaScript

For background information on the decorator pattern check the Wikipedia article or for PHP implementations, here and here.

Motivation and example use

Let's take an example - you've created a class and released to the world. You want people to be able to easily build upon it and also release to the world. Some other folks can take your base class and selectively choose from the extensions only those that make sense for them. The example I choose is a class that does something on some text, beautifies it. Makes sure that there's always a space after the dots in a sentence and the dashes are also surrounded by spaces and so on. A Yahoo developer might want to add a feature (a decorator) that also adds an exclamation after the word Yahoo. A Spanish-speaking developer might add a feature where the exclamation sentences have the flipped exclamation mark before them. ¡Hola! Some people might add other language-specific or business-specific functionality. At the end, a new user of the class should be able to easily use the available features he likes, and leave out the rest. Let's see how the "client" code might look like:

 
// create an instance of the base class
// and initialize with some text
var t = new TextBeautifier.Core('Some text.And some more.Yeah, baby,yeah-Yahoo');
// get a decorator
t = t.getDecorator('Punctuation');
// get another one
t = t.getDecorator('FixDashes');
// another one
t = t.getDecorator('Yodel');
// call the method that will give
//  a beautified result
t.get(); // "Some text. And some more. Yeah, baby, yeah - Yahoo!"
// change the input text
t.set('bla-bla-blah!Huh?');
// beautify again
t.get();

The idea is that no matter how many or how little decorators you add, the basic functionality (setting text input, getting beautified output) remains the same. Later you might want to add new decorators or remove some, but the method calls to get/set are still unchanged.

Implementation

So let's see how this could be implemented in JavaScript. We have a base (core) class that provides functionality, in this simple case just setting and getting a text. In addition to that, the core class has a method getDecorator() that is used to add new decorating functionality.

// just some namespeces
TextBeautifier = {}; 
TextBeautifier.Decorator = {};
 
// constructor of the base class
TextBeautifier.Core = function (text){
  // store the text
  TextBeautifier.Core.prototype.text = text;
  // the basic get method
  TextBeautifier.Core.prototype.get = function(){
    return this.text; // might as well be TextBeautifier.Core.prototype.text
  };
  // the set [new text] method
  TextBeautifier.Core.prototype.set = function(t){
    TextBeautifier.Core.prototype.text = t;
  }
  // method that handles the decoration stuff
  // this method accepts the name of the decorator
  TextBeautifier.Core.prototype.getDecorator = function(deco){
    // get the longer name of the decorator class constructor
    var child = TextBeautifier.Decorator[deco];
    // the decorator extends (inherits from)
    //    the parent class
    child.prototype = this;
    // return an instance of the new decorator
    return new child;
  }
}

The method getDecorator() is the most interesting since it contains the decoration logic. So what happens when we have an instance of the Core class, called t and we say:

t = t.getDecorator('Punctuation');

The getDecorator does the following:

  1. Figures out the name of the constructor of the new decorator, in this case it's TextBeautifier.Decorator.Punctuation
  2. Makes the Punctuation class inherit the Core by setting the Puncuation prototype to point to this which is an instance of the Core class
  3. Creates a Punctuation instance and returns it

So after the line:

t = t.getDecorator('Punctuation');

now t is an instance of the Punctuation class which also has everything its parent Core class had.

Next, we add another decorator;

t = t.getDecorator('FixDashes');

Now t becomes an instance of the FixDashes class which inherits Punctuation, which in turn inherits Core. And so on. At first this might look like a normal inheritance chain but the difference is that the order of inheritance doesn't matter and can be changed at any time. The other beauty of this implementation of the decorator pattern is how simple the decorators are to implement. Inheritance is already taken care of by the Core class. A decorator simply implements a get() method that takes the output of the previous decorator and further beautifies is. Like:

// implementing a decorator
TextBeautifier.Decorator.Punctuation = function(){
  // defining own get() method
  this.get = function(){
    // first get whatever was done so far by the previous 
    // class in the chain
    var text = TextBeautifier.Decorator.Punctuation.prototype.get();
    //  then do your work and return
    return text.replace(/([\.,!])\s*/g, '$1 ');
  }
}

Now let's implement another decorator

TextBeautifier.Decorator.FixDashes = function(){
  this.get = function(){
    var text = TextBeautifier.Decorator.FixDashes.prototype.get();
    return text.replace(/\s*-\s*/g, ' - ');
  }
}

Same thing - get what your parent does and further decorate the output. In this case the parent is Punctuation. Another decorator, same thing:

TextBeautifier.Decorator.Yodel = function(){
  this.get = function(){
    var text = TextBeautifier.Decorator.Yodel.prototype.get();
    return text.replace(/Yahoo/g, 'Yahoo!');
  }
}

C'est tout

Simple and elegant. Add decorators at will and combine to taste. This example used only one "working" method - get() - but the pattern doesn't force you to extend only one method, you can have as many methods that do something and can be further extended by decorators.

In this implementation you can also get the original raw text at any time using TextBeautifier.Core.prototype.get(), even in cases where you set() it further in the chain, this is because all decorators defined their own get(), they didn't mess up with the original prototype's get().

A Christmas tree

I couldn't help but add another illustration. Say you have a class ChristmasTree and decorators Angel, HugeStar, BlueBalls, RedBalls, GoldenBalls, SilverBalls, SilverDust, GoldenGarlands, LightBulbs... you get the point. Then you go:

var tree2008 = new ChristmasTree();
tree2008.getDecorator('Angel');
tree2008.getDecorator('RedBalls');
tree2008.getDecorator('SilverDust');
tree2008.getDecorator('BlueBalls');
tree2008.gimme();

or

var tree_at_myparents = new ChristmasTree();
tree_at_myparents.getDecorator('HugeStar');
tree_at_myparents.getDecorator('GoldenBalls');
tree_at_myparents.getDecorator('GoldenGarlands');
tree_at_myparents.getDecorator('LightBulbs');
tree_at_myparents.gimme();

Tell your friends about this post on Facebook and Twitter

Sorry, comments disabled and hidden due to excessive spam.

Meanwhile, hit me up on twitter @stoyanstefanov