The Decorator Pattern in JavaScript
Decorator Pattern in Javascript
Another common Javascript pattern that we can take a look at is the Decorator pattern, which is sure to come in handy whether you’re doing web application development or desktop application development. It is a structural pattern that encourages code reuse and is a great way to do sub-classing. This is also used for creating additional systems in which you may add new features to objects.
Let’s start with a simple subclass. For developers who are not familiar with subclassing here’s a basic starter guide before we go through with decorators.
var subclassExample = subclassExample || {};
subclassExample = {
Person: function( firstName , lastName ){
this.firstName = firstName;
this.lastName = lastName;
this.gender = 'male'
}
}
Using that basic example, we have created an object. In traditional object oriented programming, whenever a class extends to another class it will inherit all of those class properties. In this case, if we consider object A to be the superclass then object B which extends to the superclass will be able to inherit all of the properties that object A has.
In our case, we have this subclass example of a person. Following the footsteps of OOP, we can reuse the code that we have created and use it if we want to create another class. Let’s say for example that Person is a Hero like what you see in most RPG games. Hero’s are basically people, but they have extraordinary powers that they can wield. With this example, we don’t have to write the code again that the Person has but rather we can easily reuse it.
var subclassExample = subclassExample || {};
subclassExample = {
Person: function( firstName , heroType ){
this.firstName = firstName;
this.heroType = heroType;
this.gender = 'female';
this.spec = 'intelligence';
}
}
// Create a new instance of person.
var hero1 = new subclassExample.Person( "Norman Lightbringer" , "Paladin" );
// Define a subclass constructor of a hero we would like to create.
subclassExample.Hero = function( firstName, heroType , powers ){
/*
Call the superclass constructor of the object
then use .call() to invoke the constructor.
*/
subclassExample.Person.call(this, firstName, heroType);
//Store hero powers that are not found on the Person object.
this.powers = powers;
}
subclassExample.Hero.prototype = new subclassExample.Person;
var hero2 = new subclassExample.Hero( "Jaina Winterstrife" ,"Mage" , ['Fireblast','Icebolt','Flamestrike'] );
console.log(hero2);
The Hero definition creates an object that holds true with the Person. The Hero object has the capability of overriding any inherited values from its parent object.
So we finally have a firm grasp of how subclassing works. Now let’s dive into the Decorators.
//What we're going to decorate
function PC()
{
this.cost = function ()
{
return 1000;
};
this.monitor = function ()
{
return 24 + "in";
};
}
/*Decorator 1*/
function RAM(pc,pcs)
{
var RAMCost = 20;
var baseCost = pc.cost();
pc.cost = function()
{
return baseCost + (RAMCost * pcs);
}
}
/*Decorator 2*/
function VideoCard(pc){
var baseCost = pc.cost();
pc.cost = function(){
return baseCost + 200;
};
}
/*Decorator 3*/
function Harddisk(pc)
{
var HardDiskCost = 30;
var baseCost = pc.cost();
pc.cost = function()
{
return baseCost + HardDiskCost;
};
}
var myPC = new PC();
RAM(myPC,4); // Add RAM
VideoCard(myPC); // Add Video Card
Harddisk(myPC); // add Hard Disk
console.log("Total Price: "+ myPC.cost() + " USD");
console.log(myPC.monitor());
In this example we can see the decorators are overriding the superclass’ .cost() method. Every time we add a computer part we are adding the price of the PC. It is considered as a decorator since the PC’s object constructor methods are not overwritten especially the monitor(), as well as any other properties that we may add in the near future. The PC object still remains the same.
Here’s another basic example. In this one we simply add a new property to a basic object. We didn’t follow the Open-closed Principle, but rather to remedy that problem we have created a new object which delegates the basic object.
// Basic object
var Book = {
name: 'The Witcher',
pages: 1263,
author: "Andrzej Sapkowski",
type: "High Fantasy",
binding: "paperback"
}
// Decoration function
function decorate(obj, decoration)
{
var objNew = Object.create(obj)
for(i in decoration){
objNew[i] = decoration[i]
}
return objNew
}
// Let's decorate
var MyBook = decorate(Book, {
getHeading:function()
{
return 'title: ' + this.name + '; pages: ' + this.pages
},
getPages:function()
{
if(this.pages > 500)
{
return "Book may take 1 week to read";
}
else
{
return "Book may be finished in 2 to 3 days reading";
}
},
getAuthor:function()
{
return this.author;
},
getType:function()
{
return this.type;
},
getBinding:function()
{
return this.binding;
}
})
console.log(MyBook.getHeading()); // title: great book; pages: 145
console.log(MyBook.getPages()); // false
console.log(MyBook.getAuthor());
console.log(MyBook.getBinding());
There are however drawbacks that you need to be aware of when you try to implement the decorator pattern. If poorly managed, it can complicate larger applications. However, developers can use this transparently as you can “wrap” objects decorated with added new behavior that you would add to your object.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment