Before the Ajax appears, JavaScript is rarely utilized in the traditional Web applications, and under most circumstances developers have to face up to procedure-oriented programming. So, real advanced JavaScript developers are also rare. But nowadays, with the share of the rich client Web applications becoming larger and larger, the role of JavaScript is becoming more and more outstanding and important. How to the reuse the existing JavaScript code and deal with the changing of requirements becomes the urgent problem that every JavaScript developer has to take into account. The popular design patterns put forward by GoF (Erich Gamma, Richard Helm, Ralph Johnson, and John M. Vlissides) are welcomed by developers all over the world. As for the JavaScript field, these design patterns applies in the similar way. In this article I will show you how to apply the six popular design patterns from GoF to the JavaScript programming area by related samples.
Now, let us delve into these interesting patterns one-by-one.
First of all, let us look at how the simplest yet commonly-used pattern, the Singleton pattern, is achieved in JavaScript.
(1) Concept
As a very fundamental and important pattern, the Singleton pattern is used to guarantee that there is one and only one instance for a given class, and provide an overall point to visit it.
Under a lot of practical circumstances, you usually need to insure there is one and only one instance related to a specified class. For this, traditional languages often resort to embedding a static variable in the class definition, setting up this variable in the first instance and making corresponding verifications whenever using the constructor of the instance. That is to say, no matter how many instantiations you have established, the static variable only relates to one instance. To prevent from instantiating a class more than one time, you need to declare the constructor function as private type, so that you can only establish the instance in the static method.
In the JavaScript language, although we still can specify a static method to construct an object, because we cannot make use of the "private" characteristic of the constructor function to forbid creating more than one instances, it is not as simple as that imagined to completely carry out the Singleton pattern.
(2) Example
Assume that we want to create an object which serves as a tool helper, including no business logic and any data required to be changed. Obviously, when you use such an object you do not need to instantiate the class each time -- one and only instance is enough for global invocation.
According to the analysis above, we can use JavaScript code (Listing 1) to carry out the Singleton pattern. This can insure there is one and only one instance relating to a given class, and provide an overall site to access it.
Listing 1: Establish the Singleton pattern in the JavaScript
function Singleton(){ this.property1=”hello”; this.methodl=function(x,y){ return x+y; } //define a static property Singleton._instance_=null; Singleton.getInstance=function(){ if(this._instance_==null){ //if there is no instance for class Singleton, then create one this._instance_=new Singleton(); } //return the instance of class Singleton return this._instance_; };
The getInstance method first judges whether the static property Singleton._instance_ is null. If so, then it sets up an instance of the Singleton object and stores it in property Singleton._instance_. Finally, the Singleton._instance_ property is returned.
As for the usage of the Singleton class, it is easy:
Listing 2
//return only one instance var instA =Singleton.getInstance();
Regrettably, the above code cannot prevent a user from instantiating the Singleton class directly. For example, it is still OK to set up an instance of the Singleton class through the following sentence.
Listing 3
var instA =new Singleton ();
And later on, it is also valid to make the following invocations.
Listing 4
alert(typeof(instA)); alert(instA.property1);
Therefore, we need a further modification with the definition of the Singleton structure so that you can only instantiate the Singleton class in the Singleton.getInstance method. Please look at the following solution.
Listing 5
function Singleton(){ if(Singleton.caller!=Singleton.getInstance){ throw new Error(”Can not new Singleton instance!”); } …… (omitted) }
Now, whenever the user tries to establish several instances, an exception is thrown out. However, it still feels some awkward, doesn't it? But anyway, we have already used the above-mentioned solution to achieve our initial purpose-- creating one and only instance of a given class.
Next, we start to discuss the third method (also a second to none method). This kind of method skillfully makes use of JavaScript's support toward "anonymous" function to indirectly achieve the target of forbidding the access to the constructor function of class Singleton, which preferably imitates the characteristic of private constructor function and perfectly solves the problem that JavaScript carries out the Singleton pattern. Listing 6 below shows this kind of best solution.
Listing 6: with the help of "anonymous" function to implement the Singleton pattern in JavaScript
<script type="text/javascript"> (function(){ //SingletonFactory Interface SingletonFactory = { getInstance : getInstance } //private classes function SingletonObject() { SingletonObject.prototype.methodA = function() { alert('methodA'); } SingletonObject.prototype.methodB = function() { alert('methodB'); } SingletonObject.instance = this; } //SingletonFactory implementions function getInstance() { if(SingletonObject.instance == null) return new SingletonObject(); else return SingletonObject.instance; } })(); var instA = null; try { alert("Try to create an instance using new SingletonObject()!"); instA = new SingletonObject(); } catch(e){ alert("You cannot access the constructor of SingletonObject externally!"); } //create the target instance with the help of the // static method from the Factory instA = SingletonFactory.getInstance(); var instB = SingletonFactory.getInstance(); instA.methodA(); instB.methodA(); alert(instA == instB); //OK </script>
Ha, the third solution finally finds out a way to it by digging into the ability of "poor" JavaScript, but finally obtains decent effect.
First of all, let us look at the definition of the Factory Method pattern.
According to the GoF's definition, the purpose of the Factory Method pattern is to define an interface to create objects, while it is the sub classes' responsibility to decide which class to instantiate. More accurately, the Factory Method pattern transfers the responsibility of creating instantiations to the factory class, and makes use of the abstract principle to delay the instantiation behavior to concrete factories.
Under some conditions, we really cannot make sure the object to establish corresponds to which class' instantiation. Such a typical example is to establish the XMLHttpRequest (XHR) object in the Ajax applications, because everyone knows the implementation of the XHR object is different in different browsers.
With the help of the Factory Method pattern, we can easily establish the generally-used XHR object in JavaScript. The corresponding code of such an implementation is as follows.
Listing 7
function XMLHttpFactory(){} XMLHttpFactorv.CreateXMLHttp=function() { if(typeof XMLHttpRequest!=”undefined”){ //for browsers( such as Firefox) that support the XMLHttpRequest object return new XMLHttpRequest(); } else if(typeof window.ActiveXObject !=”undefined”) { // for browsers( such as IE) that support // the ActiveX object return new ActiveXObject(“MSXML2.XMLHttp”); } }
Then, using the following code you can easily judge the type of the target browser and then establish the proper XHR object.
var xmlhttp=XMLHttpFactory.createXMLHttp();
Henceforth, you can carry out the server-side invocation in your Ajax styled applications according to the XHR object established above.
First of all, let us, as usual, look at the definition of the Decorator pattern.
The Decorator pattern is one of the seven Structural patterns which are concerned with how classes and objects are composed of larger structures (the other six Structural patterns are Proxy, Bridge, Composite, Flyweight, Adapter, Façade, respectively). The Decorator pattern makes full use of the advantages of inheritance and aggregation, whose main purpose is to attach new state and behavior to a ready-made object dynamically while not modifying the original behaviors. This can be compared to an adornment worker who is ready to decorate a wall of a new residence with colorful pigment.
Interesting thing is that we can apply the Decorator pattern to the Aspect Oriented Programming (AOP) in the JavaScript scenario.
Author's Note: Aspect-oriented programming, AOP in short, is a new means of method to improve the existing pattern and discover new ones. According to Wikipedia, aspect-oriented programming (AOP) is a programming paradigm that increases modularity by allowing the separation of cross-cutting concerns. Separation of concerns entails breaking down a program into distinct parts (so-called concerns, cohesive areas of functionality). All programming paradigms support some level of grouping and encapsulation of concerns into separate, independent entities by providing new abstractions (e.g. procedures, modules, classes, methods) that can be used to represent these concerns. But some concerns defy these forms of encapsulation and are called crosscutting concerns because they "cut across" multiple abstractions in a program.
Next, let us look at the code shown in Listing 8 below.
Listing 8: Use the Decorator pattern to achieve AOP in JavaScript
function Decorator(object){ object.setup=function(method){ //if never execute the Setup method, then make initialization if(!(‘_’+method in object)){ //reserve the existing methods object[‘_’+method]=object[method]; //initialize the lists for before and after advice respectively object[‘beforeAdvice_’+method]=[]; object[‘afterAdvice_’+method] =[]; // override the <span class=Bold>method</span> method object[method]=function(){ //establish the before advice list var before=this[‘beforeAdvice_’+method]; // establish the after advice list var after=this[‘afterAdvice_’+method]; //used to hold the returned value var ret; //invoke the functions in the before advice for(var i=0; i<before.length; i++) { before[i].call(this,arguments); } // invoke the initial functions ret=this[‘_’ +method](arguments); // invoke the functions in the after advice for (var i=; i<after.1ength; i++){ after[i].call(this, arguments); } //return the value of the initial method return ret; } } }; //attach the before advice object.addBeforeAdvice=function(method, f) { object.setup(method); //the last before advice attached will be invoked first object[‘beforeAdvice_’ +method].unshift(f); }; // attach the after advice object.addAfterAdvice=function(method, f) { object.Setup(method); // the last after advice attached will be invoked first object[‘afterAdvice_’ +method].push(f); }; }
The basic idea behind the Decorator function above is to substitute the original function for a now one and in the one add invocations to the before and after functions. Below is a concrete example to explain the usage of the Decorator function. For convenience, we should first define a class named testClass.
Listing 9
var testClass = function() { testClass.prototype.pl=”hello”; testClass.prototype.ml =function() { alert(this.p1); } };
If you wish to place before or after notifications upon all the instantiations of class testClass, then you need to do some processing with the testClass.prototype attribute.
Listing 10
Decorator(testClass.prototype); testClass.prototype.addBeforeAdvice(“m1”, function() { alert(“beforeAdvice”); }); testClass.prototype.addAfterAdvice(“m1”, function() { alert(”afterAdvice”); });
At this time, if you create an instantiation of class testClass and invoke its ml method, then the application will output strings "beforeAdvice," "hello," and"afterAdvice" in order.
Listing 11
var t=new testClass (); //output 'beforeAdvice','hello','afterAdvice' t.ml();
If you only want to place before or after notifications upon a specified instantiation of class testClass, then you can directly do with this instantiation, as shown below.
var t=new testClass(); Decorator(t); t.addBeforeAdvice(“ml” ,function](){ alert(“beforeAdvice”); }); t.addAfterAdvice(“ml”, function(){ alert(“afterAdvice”); });
That is it.
As above, let us first look at the definition of the Observer pattern.
The Observer pattern defines a relationship between objects so that when one changes its state, all the others are notified accordingly. Generally, you are suggested to consider leveraging the Observer pattern under the following conditions.
· When an abstract model contains two parts, one depending on the other, you need to seal these twos independently in order to change and reuse them respectively and independently.
· When you want to change an object then in the meantime you have to change other objects, but do not know how many objects need the related change.
· When an object has to notify other objects, but it cannot suppose who the other objects are. In other words, you do not hope that these objects are closely coupled.
Underneath, we are going to apply the Observer pattern to a simple yet typical JavaScript event handling mechanism. First, we need to do some preparation, namely expanding the Array object with two methods (i.e. indexOf and removeAt below), as shown in Listing 12.
Listing 12 - Expand the Array object
//expand the properties of the destination object //, copy all the properties of the source object //, to the destination Object.extend=function(destination,source){ for(property in source){ destination[property]=source[property]; } return destination; } // expand the built-in Array object Object.extend(Array.prototype,{ //retrieve the object in the array indexOf: function (object){ for(var i=0,1ength=this.length; i<length; i++) if(this[i]==object) return i; return -1; }, //remove the specified element in the array removeAt: function (index){ if(index<0 || index>=this.length) return null; switch(index){ case 0: return this.shift(); break; case this.length -1: return this.pop(); break; default: var head=this.s1ice(0,index); var tail=this.s1ice(index+1); var ele=this[index]; this=head.concat(tail); return ele; break; } } });
Next, define two classes, i.e. Observer and Subject. The code related to the Observer class is shown below:
//the Observer class function Observer(){} object.extend(Observer.prototype, { //note that the later instantiation Observer will override this Update method Update:function(){ return; } });
The code related to the Subject class is as follows.
Listing 13
// the Subject class function Subject(){} Object.extend(Subject.prototype,{ //an array of the Observer objects observers:[], // notify per Observer object to execute the Update method notify:function(context){ for(var i=0; i<this.observers.length; i++) this.observers[i].Update(context); }, //add a new Observer addObserver:function(observer) { if(!observer.Update) throw new Error(“addObserver error!”); this.observers.push(observer); }, //delete the existing Observer removeObserver:function(Observer) { if(!observer.Update) throw new Error(“removeObserver error!”); this.observers.removeAt(this.observers.indexOf(observer)); }, });
OK, for now we have defined all the objects relating to the Observer pattern. The following lists the steps to carry out the Observer pattern:
· Establish an instantiation of the Subject class.
· Establish several instantiations of the Observer object, overriding their Update methods respectively.
· Subscribe all the Observer objects to the Subject object.
· Send out a notice through the Subject object, when the Observers objects are subscribed to the Subject object will execute their Update methods respectively.
· If you need to cancel a certain Observer object's subscription for the Subject object, you can invoke the removeObserver method.
Finally, we are to set up an example to show you the application of the Observer pattern, as well as the objects defined above. First, Establish a Subject object and define a publishEvent method for it.
Listing 14
//Create a new Subject object var concreteSubject=new Subject(); // define a new publishEvent method with which to notify all the Observer of the related data concreteSubject.publishEvent=function(data){ document.write(“published:”+data); this.notify(data); }
Then, establish two Observer objects; overlay their Update methods respectively.
Listing 15
// create a new Subject object var concreteSubject=new Subject(); // define a new publishEvent method with which to notify all the Observer of the related data concreteSubject.publishEvent=function(data) ( document.write(“published:”+data); this.notify(data); } // Create a new Observer object and override its Update method var concreteObserverl=new Observer(); concreteObserverl.Update=function(data){ document.write(“concreteObserverl received:”+data); } //subscribe concreteObserverl to concreteSubject concreteSubject.addObserver(concreteObserverl); // create the second Observer object and override its Update method var concreteObserver2=new Observer(); concreteObserver2.Update=function(data){ document.write(“concreteObserver2 received:”+data); } //subscribe concreteObserver2 to concreteSubject concreteSubject.addObserver(concreteObserver2);
Now, let us carry out the publishEvent method of the concreteSubject object, sending out a notice.
Listing 16
concreteSubject.publishEvent(“msg”);
At this time, the application will output the following.
Listing 17
published:msg concreteObserverl received:msg concreteObserver2 receired:msg
Such results proved that concreteObserverl and concreteObserver both received a notice from the concreteSubject object. Next, you can use the removeObserver method to cancel a subscription for the concreteSubject object.
Listing 18
concreteSubject.removeObserver(concreteObserver2);
Again send out a notice:
Listing 19
This time, only concreteObserver receives the notice and the application will output the following.
Listing 20
published:msg concreteObserver1 receired:msg
Using the Observer pattern can implement the typical event mechanism in the JavaScript environment. Those who are acquainted with the Ajax development may know of the two famous client-side Ajax frameworks--Prototype and Dojo. In fact, the two frameworks both adopt the similar means herein to introduce their respective event mechanisms. Readers who have interests with this can study further.
First of all, let us still look at the definition of the Façade pattern.
The Façade pattern can define the homologous interfaces for a set of sub systems. The true secrets behind the Façade pattern lies in that it can define a high-level interface with which to make all the sub systems more easily to use. A typical illustration of the Façade pattern in developing Web applications is to implement a uniform API with which to remove the differences of various browsers in realizing the concrete characteristics.
Let us next still see a related example.
In constructing the Ajax based Web applications, when we use the XHR object to request the server-side data, we usually utilize the following steps to code:
· Create the XHR object.
· Register the related callback function.
· Set the request mode (POST or GET), URL, and pattern (synchronously or asynchronously) with the open method.
· Send out the request with the send method.
· Supervise the states of the request, and if the special request is met, then perform the specified function.
As you've guessed here, using the XHR object is rather complicated, in each of the above steps you have to consider the browser-compatible related problems.
Below, we will show you the basic application of the Façade pattern through a simple example. The target of the example is to output a line to welcome information hinting the browser type the current user is using. For the sake of simplicity, we are only to distinguish the IE from other types of browsers in the example. The key and related code is shown as follows:
Listing 21
//IE function IEBrowser() { this.hello=function(){ alert(”IE browser”); } } //Non-IE browser function NonIEBrowser() ( this.hello =function(){ alert("Non-IE browser"); } } //The Façade object var Facade={}; Facade.hello=function(){ var browser; // IE if(window.ActiveXObject) browser=new IEBrowser(); //Non-IE browser else browser=new NonIEBrowser(); Browser.hello(); };
The "Facade.hello" method above establishes different browser objects according to the browser type and invokes the corresponding hello methods. So, for the developers, they do not need to consider the browser type, but only focus on invoking the hello method of the Facade object.
Moreover, the IE browser and Non-IE browser are two types of independent classes, both of whose internal realizations may change in the future, but this will not influence the exterior object Facade to invoke them, namely the "Facade.hello" method related code does not need to be changed.
As usual, let us look at the definition of the Command Pattern.
The Command pattern encapsulates requests, invocations, or operations into an object. In detail, the Command pattern allows the system to parameterize the clients using different claims, queuing up the requests, recording request records, etc. The Command pattern can separate the responsibilities of issuing commands from those of executing commands, delegating them to the different objects.
In this section, I will explain the application of Command pattern by dissecting the design idea of constructing a JavaScript based calculator application.
What first comes to my mind is to define the following Calculator object which includes four methods relating to the four arithmetic operations respectively.
Listing 22
var Calculator={ // addition add: function(x,y) { return x+y; }, // subtraction substract: function(x, y) { return x-y; }, // multiplication multiply: function(x, y) { return x*y; }, // division divide: function(x, y) { return x/y; }, };
To be honest, based on the above Calculator object, we can carry out the four basic arithmetic operations by directly invoking the methods, such as "Calculator.add," and "Calculator.substract," etc. In some circumstances, however, we do not expect to invoke the inner methods inside the Calculator object directly. The reason for this is apparent: this will increase the dependencies among objects. In another word, if the internal logics of the Calculator object change, then all the code scripts that carry out the calculations associated with the Calculator have to be modified in the meantime. This, as you may imagine, will violate the principle advocated by OOP-- to loosely couple the objects as much as possible.
According to the design pattern related theories put forward by GoF, we can leverage the Command pattern to improve the above design. Here, the basic idea of the Command pattern is: take any type of calculations as a request to the Calculator object, the contents of a request including the type of operation and the related operands. Hence, using the code below you can get the result of the formula of "1+2."
Listing 23
Calculator.calc({ type: "add", // addition op1:1, //the first operand is 1 op2:2 // the second operand is 2 });
According to the format above, we can conclude the definition for the "Calculator.calc" method as follows:
Listing 24
Calculator.calc=function(command) { return Calculator[command.type](command.opl,command.op2); };
Consequently, we can use the following four "Calculator.calc" methods to carry out each kind of above-mentioned operations.
Listing 25
Calculator.calc({type: "add",opl:1,op2:1}); Calculator.calc({type: "Substract",opl:6,op2:2}); Calculator.calc({type: "multiply",opl:5,op2:2)); Calculator.calc({type: "divide",opl:8,op2:4));
The thought of Design Pattern based development has been put forward for quite a few years, but just caused the attention of the forefront developers in recent years, which can be proved with the numerous data from the Internet. The various design patterns are based upon the object-oriented programming, so they are not easy to grasp. As you may imagine, only through continuous software development practice can we finally apply various design patterns flexibly.
Design patterns are not any language specific, so equally apply in the JavaScript area, only with very few materials focusing on this aspect. This article based upon common JavaScript examples on the client side and discussed six kinds of typical design patterns, i.e. Singleton, Factory Method, Decorator, Observer, Facade, and Command.
User Comments
No comments posted yet.