AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1782&pId=-1
Working with GoF's Design Patterns in JavaScript Programming
page
by Xianzhong Zhu
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 79878/ 103

Introduction

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.

Working with the Singleton Pattern

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.

Working with the Factory Method Pattern

First of all, let us look at the definition of the Factory Method pattern.

(1) Concept

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.

(2) Example

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.

Working with the Decorator Pattern

First of all, let us, as usual, look at the definition of the Decorator pattern.

 (1) Concept

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.

 (2) Example

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.

Working with the Observer Pattern

As above, let us first look at the definition of the Observer pattern.

(1) Concept

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.

(2) Example

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

concreteSubject.publishEvent(“msg”);

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.

Working with the Façade Pattern

First of all, let us still look at the definition of the Façade pattern.

(1) Concept

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.

 (2) 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.

Working with the Command Pattern

As usual, let us look at the definition of the Command Pattern.

(1) Concept

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.

(2) Example

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));
Conclusion

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.


Product Spotlight
Product Spotlight 

©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-19 5:31:47 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search