
The decorator pattern
The primary idea behind the decorator pattern is that you start your design with a plain object, which has some basic functionality. As the design evolves, you can use existing decorators to enhance your plain object. This is a very popular pattern in the OO world and especially in Java. Let's take an example of BasicServer
—a server with very basic functionality. This basic functionality can be decorated to serve specific purposes. We can have two different cases where this server can serve both PHP and Node.js and serve them on different ports. These different functionality are decorated to the basic server:
var phpServer = new BasicServer(); phpServer = phpServer.decorate('reverseProxy'); phpServer = phpServer.decorate('servePHP'); phpServer = phpServer.decorate('80'); phpServer = phpServer.decorate('serveStaticAssets'); phpServer.init();
The Node.js server will have something as follows:
var nodeServer = new BasicServer(); nodeServer = nodeServer.decorate('serveNode'); nodeServer = nodeServer.decorate('3000'); nodeServer.init();
There are several ways in which the decorator pattern is implemented in JavaScript. We will discuss a method where the pattern is implemented by a list and does not rely on inheritance or method call chain:
//Implement BasicServer that does the bare minimum function BasicServer() { this.pid = 1; console.log("Initializing basic Server"); this.decorators_list = []; //Empty list of decorators } //List of all decorators BasicServer.decorators = {}; //Add each decorator to the list of BasicServer's decorators //Each decorator in this list will be applied on the BasicServer instance BasicServer.decorators.reverseProxy = { init: function(pid) { console.log("Started Reverse Proxy"); return pid + 1; } }; BasicServer.decorators.servePHP = { init: function(pid) { console.log("Started serving PHP"); return pid + 1; } }; BasicServer.decorators.serveNode = { init: function(pid) { console.log("Started serving Node"); return pid + 1; } }; //Push the decorator to this list everytime decorate() is called BasicServer.prototype.decorate = function(decorator) { this.decorators_list.push(decorator); }; //init() method looks through all the applied decorators on BasicServer //and executes init() method on all of them BasicServer.prototype.init = function () { var running_processes = 0; var pid = this.pid; for (i = 0; i < this.decorators_list.length; i += 1) { decorator_name = this.decorators_list[i]; running_processes = BasicServer.decorators[decorator_name].init(pid); } return running_processes; }; //Create server to serve PHP var phpServer = new BasicServer(); phpServer.decorate('reverseProxy'); phpServer.decorate('servePHP'); total_processes = phpServer.init(); console.log(total_processes); //Create server to serve Node var nodeServer = new BasicServer(); nodeServer.decorate('serveNode'); nodeServer.init(); total_processes = phpServer.init(); console.log(total_processes);
BasicServer.decorate()
and BasicServer.init()
are the two methods where the real stuff happens. We push all decorators being applied to the list of decorators for BasicServer
. In the init()
method, we execute or apply each decorator's init()
method from this list of decorators. This is a cleaner approach to decorator patterns that does not use inheritance. This method was described by Stoyan Stefanov in his book, JavaScript Patterns, O'Reilly Media, and has gained prominence among JavaScript developers due to its simplicity.