Click here to Skip to main content
15,861,125 members
Articles / Web Development / HTML
Article

Observer Design Pattern Using JavaScript

Rate me:
Please Sign up or sign in to vote.
4.97/5 (117 votes)
26 Apr 20065 min read 367K   1.2K   154   36
Ensuring a crisp boundary exists between objects, enabling greater reuse and system maintainability.

Introduction

Anyone who has ever worked with JavaScript for any amount of time should be familiar with the task of creating custom JavaScript objects. People who are not familiar with OOP in JavaScript can read a brief introduction about it here. This article deals with the implementation of the Observer pattern using JavaScript.

A brief introduction to JavaScript

JavaScript is a prototype-based scripting language (originally called LiveScript) with a syntax loosely based on C, developed by Netscape Communications, for use with the Navigator browser. Like C, the language has no input or output constructs of its own. Where C relies on standard I/O libraries, the JavaScript engine relies on a host environment into which it is embedded. This scripting language accommodates what other languages might call procedures, subroutines, and functions, all in one type of structure: the custom function. One major use of web-based JavaScript is to interact with the Document Object Model (DOM) of a web page to perform tasks not possible using HTML alone. JScript is the Microsoft equivalent of Netscape's JavaScript, for use with Microsoft's Internet Explorer.

Create a custom object in JavaScript

Creating a new JavaScript object consists of two steps. First, you need to create a function whose name will be the name of the new class. This function is typically referred to as a constructor. Second, you have to create as instance of the object using the new operator followed by the object name and any parameters it requires. The following code defines a Person function, and then creates a Person object using the new operator:

JavaScript
function</CODE> Person( name, surname )
{
   this.name    = name;
   this.surname = surname;
}

var salvo = new Person( ‘Salvatore’, ‘Vetro’ );

this points to the current object instance with which you are working, thus allowing you to add and modify properties on the object.

How can you add a method to an object?

In JavaScript, every object that can be created by calling a constructor function has an associated prototype property. The syntax for adding a new method is:

JavaScript
customeObject.prototype.newMethodName = function;

// Specific case

Person.prototype.Speak = function(){...}

If you add a method to the prototype for an object, then all objects created with that object's constructor function will have that new method. Note that prototype is itself an object, and can be assigned properties and methods via the object literal syntax:

JavaScript
function NewObject()
{
   alert("I am a new object.");
}

NewObject.prototype = 
{
   alert1: function(str){alert(str);}, //new method

   name: 'As you want', //new property

   alert2: function(){alert("Bye.");} //new method
};

var newObject = new NewObject();

newObject.alert1("Ciao");

newObject.name;

newObject.alert2();

Each time your script attempts to read or write a property of an object, JavaScript follows a specific sequence in search of a match for the property name. The sequence is as follows:

  • If the property has a value assigned to the current (local) object, this is the value to use.
  • If there is no local value, check the value of the property’s prototype of the object’s constructor.
  • Continue up the prototype chain until either a match of the property is found (with a value assigned to it) or the search reaches the native Object object. Therefore, if you change the value of a constructor’s prototype property and you do not override the property value in an instance of that constructor, JavaScript returns the current value of the constructor’s prototype property.

Observer Design Pattern class diagram

The Observer design pattern defines a one-to-many dependency between a subject object and any number of observer objects, so that when the subject object changes state, all its observer objects are notified and updated automatically.

Image 1

  • Participants
    • Subject
      • Knows its observers. Any number of Observer objects may observe a Subject.
      • Provides an interface for attaching and detaching Observer objects.
    • Observer
      • Defines an updating interface for objects that should be notified of changes in a Subject.
    • ConcreteSubject
      • Stores the state of interest to ConcreteObserver objects.
      • Sends a notification to its observers when its state changes.
    • ConcreteObserver
      • Maintains a reference to a ConcreteSubject object.
      • Stores state that should stay consistent with the subject's.
      • Implements the Observer updating interface to keep its state consistent with the Subject's.
  • Collaborations
    • ConcreteSubject notifies its observers whenever a change occurs that could make its Observers' state inconsistent with its own.
    • After being informed of a change in the concrete subject, a ConcreteObserver object may query the subject for information. ConcreteObserver uses this information to reconcile its state with that of the Subject.

Observer Design Pattern sequence diagram

The following interaction diagram illustrates the collaborations between a Subject and two Observers:

Image 2

The Observer object that initiates the change request postpones its update until it gets a notification from the Subject. Notify is not always called by the subject. It can be called by an Observer or by another kind of object entirely.

What are we going to do?

Now you know what the Observer design pattern is and how you can create custom objects using JavaScript. As you can see in the class diagram, you have to define two methods (Attach and Detach) inside the Observer class. To do that, you need a collection to perform attach/detach operations. It is time to write your first JavaScript ArrayList. First of all, you have to define what the ArrayList should be able to do.

Count - Add - GetAt - Clear - RemoveAt - Insert - IndexOf - LastIndexOf.

JavaScript
function ArrayList()
{
   this.aList = []; //initialize with an empty array
}
        
ArrayList.prototype.Count = function()
{
   return this.aList.length;
}
        
ArrayList.prototype.Add = function( object )
{
   //Object are placed at the end of the array
   return this.aList.push( object );
}

ArrayList.prototype.GetAt = function( index ) //Index must be a number
{
   if( index > -1 && index < this.aList.length )
      return this.aList[index];
   else
      return undefined; //Out of bound array, return undefined
}
        
ArrayList.prototype.Clear = function()
{
   this.aList = [];
}

ArrayList.prototype.RemoveAt = function ( index ) // index must be a number
{
   var m_count = this.aList.length;
            
   if ( m_count > 0 && index > -1 && index < this.aList.length ) 
   {
      switch( index )
      {
         case 0:
            this.aList.shift();
            break;
         case m_count - 1:
            this.aList.pop();
            break;
         default:
            var head   = this.aList.slice( 0, index );
            var tail   = this.aList.slice( index + 1 );
            this.aList = head.concat( tail );
            break;
      }
   }
}

ArrayList.prototype.Insert = function ( object, index )
{
   var m_count       = this.aList.length;
   var m_returnValue = -1;

   if ( index > -1 && index <= m_count ) 
   {
      switch(index)
      {
         case 0:
            this.aList.unshift(object);
            m_returnValue = 0;
            break;
         case m_count:
            this.aList.push(object);
            m_returnValue = m_count;
            break;
         default:
            var head      = this.aList.slice(0, index - 1);
            var tail      = this.aList.slice(index);
            this.aList    = this.aList.concat(tail.unshift(object));
            m_returnValue = index;
            break;
      }
   }
            
   return m_returnValue;
}

ArrayList.prototype.IndexOf = function( object, startIndex )
{
   var m_count       = this.aList.length;
   var m_returnValue = - 1;
            
   if ( startIndex > -1 && startIndex < m_count ) 
   {
      var i = startIndex;

      while( i < m_count )
      {
         if ( this.aList[i] == object )
         {
            m_returnValue = i;
            break;
         }
                    
         i++;
      }
   }
            
   return m_returnValue;
}
        
        
ArrayList.prototype.LastIndexOf = function( object, startIndex )
{
   var m_count       = this.aList.length;
   var m_returnValue = - 1;
            
   if ( startIndex > -1 && startIndex < m_count ) 
   {
      var i = m_count - 1;
                
      while( i >= startIndex )
      {
         if ( this.aList[i] == object )
         {
            m_returnValue = i;
            break;
         }
                    
         i--;
      }
   }
            
   return m_returnValue;
}

Well done! Now you can create both the Observer class and the Subject class of the Observer design pattern.

Observer class of the Observer Design Pattern

You just have to define the method Update.

JavaScript
function Observer()
{
   this.Update = function()
   {
      return;
   }
}

Subject class of the Observer Design Pattern

OK, let's go define the three main methods: Notify, AddObserver, and RemoveObserver.

JavaScript
function Subject()
{
   this.observers = new ArrayList();
}

Subject.prototype.Notify = function( context )
{
   var m_count = this.observers.Count();
            
   for( var i = 0; i < m_count; i++ )
      this.observers.GetAt(i).Update( context );
}

Subject.prototype.AddObserver = function( observer )
{
   if( !observer.Update )
      throw 'Wrong parameter';

   this.observers.Add( observer );
}

Subject.prototype.RemoveObserver = function( observer )
{
   if( !observer.Update )
      throw 'Wrong parameter';
   
   this.observers.RemoveAt(this.observers.IndexOf( observer, 0 ));
}

Inheritance in JavaScript

There are some methods to simulate inheritance using JavaScript. A very simple way is to define a simple method called inherits that you can use to copy all the properties and methods of an object to another object.

JavaScript
function inherits(base, extension)
{
   for ( var property in base )
   {
      try
      {
         extension[property] = base[property];
      }
      catch( warning ){}
   }
}

A simple implementation

Now you need to implement a client so that you can attach observers to the concrete subject. For example, you could create a simple application that defines a main checkbox as observable and some checkboxes as observers. When the concrete subject will change its own state, it is going to notify what happened to the observers. Any observer being decoupled from the subject will manage, independently of each other, the notification.

JavaScript
/************* Concrete Subject *************/

var mainCheck = document.createElement("INPUT");

mainCheck.type = 'checkbox';
mainCheck.id   = 'MainCheck';
            
inherits(new Subject(), mainCheck)

mainCheck["onclick"] = new Function("mainCheck.Notify(mainCheck.checked)");
JavaScript
/**************** Observer ****************/</CODE>

var obsCheck1 = document.createElement("INPUT");
var obsCheck2 = document.createElement("INPUT");

obsCheck1.type = 'checkbox';
obsCheck1.id   = 'Obs1';

obsCheck2.type = 'checkbox';
obsCheck2.id   = 'Obs2';
            
inherits(new Observer(), obsCheck1)
inherits(new Observer(), obsCheck2)

obsCheck1.Update = function(value)
{
   this.checked = value;
}

obsCheck2.Update = function(value)
{
   this.checked = value;
    
   //Add ........
}
            
mainCheck.AddObserver(obsCheck1);
mainCheck.AddObserver(obsCheck2);

Enclosed example

Image 3

In the specific case, any Observer manages the notification in the same manner. When the concrete Subject notifies the Observers about the new state, any Observer changes its own state using the observable state as its new value.

Collaboration

This article could be improved. Please contact me for any suggestions.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Italy Italy
Salvo is a software architect working in Milan, Italy.

He enjoys design infrastructures based on object oriented paradigm.
At the moment he is managing the integration of “ERP Business Processes” with other applications developing designing business services that are service-oriented architecture compliant.

Comments and Discussions

 
Generalexcellent Pin
magiccoder1-Jan-13 23:12
magiccoder1-Jan-13 23:12 
QuestionObserver Pattern Pin
soumya Ranjan Nayak14-Sep-12 3:17
soumya Ranjan Nayak14-Sep-12 3:17 
GeneralMy vote of 4 Pin
Rupesh Kumar Tiwari21-Sep-11 6:05
Rupesh Kumar Tiwari21-Sep-11 6:05 
AnswerJQuery Pin
Member 1137657114-Jan-15 20:44
Member 1137657114-Jan-15 20:44 
GeneralMy vote of 5 Pin
gary.flu917-Jan-11 22:55
gary.flu917-Jan-11 22:55 
GeneralA contribution [modified] Pin
Ingemar Nilsson30-May-07 1:49
Ingemar Nilsson30-May-07 1:49 
GeneralExcellent.. Pin
Meysam Gharanfoli21-May-07 23:23
Meysam Gharanfoli21-May-07 23:23 
GeneralAnonymous function observers Pin
Ben Allfree6-Oct-06 4:13
Ben Allfree6-Oct-06 4:13 
GeneralSlight problem in Opera 9 Pin
Franck Quintana8-Sep-06 14:59
Franck Quintana8-Sep-06 14:59 
QuestionObserver Class Pin
DFU2321-Jul-06 5:37
DFU2321-Jul-06 5:37 
In the base Observer class what is the point of defining an Update function that will always (should always) be overwritten/overridden by the inheriting/implementing class? Is it just to mimic the observer pattern where classes will implement the interface of an Observer?

I just want to make sure that I understand the code in your example and that I understand what all is going on with the event flow. Can you possibly give another real world example of where this could be beneficial in a web application?

Thanks for great the article!





Wally Atkins
Newport News, VA, USA
http://wallyatkins.com

GeneralTake my 5 Pin
HimaBindu Vejella9-Jul-06 22:56
HimaBindu Vejella9-Jul-06 22:56 
AnswerRe: Take my 5 [modified] Pin
Salvatore Vetro17-Jul-06 5:25
Salvatore Vetro17-Jul-06 5:25 
GeneralExcellent.. Pin
Rajesh Pillai6-May-06 0:55
Rajesh Pillai6-May-06 0:55 
JokeRe: Excellent.. Pin
Salvatore Vetro7-May-06 5:42
Salvatore Vetro7-May-06 5:42 
JokeExcellent Pin
LimeyRedneck5-May-06 6:16
professionalLimeyRedneck5-May-06 6:16 
JokeRe: Excellent Pin
Salvatore Vetro5-May-06 9:26
Salvatore Vetro5-May-06 9:26 
Generalhelp me create treeview with asp.net C# Pin
chunglvcit4-May-06 23:10
chunglvcit4-May-06 23:10 
GeneralThe dark side of the Observer Pattern Pin
Alejandro Xalabarder30-Apr-06 2:57
Alejandro Xalabarder30-Apr-06 2:57 
AnswerRe: The dark side of the Observer Pattern Pin
Salvatore Vetro2-May-06 1:45
Salvatore Vetro2-May-06 1:45 
GeneralRe: The dark side of the Observer Pattern Pin
Alejandro Xalabarder2-May-06 12:13
Alejandro Xalabarder2-May-06 12:13 
JokeRe: The dark side of the Observer Pattern Pin
Salvatore Vetro2-May-06 22:15
Salvatore Vetro2-May-06 22:15 
GeneralRe: The dark side of the Observer Pattern Pin
Alejandro Xalabarder3-May-06 9:26
Alejandro Xalabarder3-May-06 9:26 
AnswerRe: The dark side of the Observer Pattern Pin
Salvatore Vetro3-May-06 10:10
Salvatore Vetro3-May-06 10:10 
GeneralRe: The dark side of the Observer Pattern Pin
amysimple21-Aug-06 22:56
amysimple21-Aug-06 22:56 
GeneralRe: The dark side of the Observer Pattern Pin
Serban Murariu30-Aug-06 20:35
Serban Murariu30-Aug-06 20:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.