- closely related to pubsub pattern, in fact, many people think of pubsub as a special use of the observer pattern
- difference is that pubsub create global message buss for communication between components, while observer is more specific and requires that observes subscibe directly to object being observed
- this pattern promotes loose coupling between objects, and is very common in javascript
Initializing the Design Pattern
- we will need a object called subject, which is the object be observed, and we need an observer
- subject will maintain list of observes and provides an api to allow for observation and notification of changes
- observer list is entity in its own right that handles adding and removing observers
Step 1: Page – observer.js
// JavaScript Document
define(function() {
'use strict';
var Observers = function() { //constructor for the observers object
this.observers = []; //we set a single property called observers, and initialize it with an empty array
};
Observers.prototype.add = function (observer) { //this is the method to add an observer
this.observers.push(observer); //receives the observer as an argument, then pushes that observer to the array
};
Observers.prototype.remove = function (observerToRemove) { //method for removing observer
this.observers = this.observers.filter(function(observer) { //javascripts filter array method is used to test each object in array. Filter method doesn't alter array its called upon, but it does create new array, so the new array is then set to the value of the old array. The callback function passed to filter method is passed current observer and is invoked for each member of array
return observer !== observerToRemove; //this method returns true if item should be included, and false if not. In this case, when observer mathes observerToRemove, returns false and observerToRemove is not included in returned array.
});
};
Observers.prototype.get = function () { //method to get list of observers
return this.observers; //just returns list of observers
};
return Observers; //then return the constructor
});
Step 2: Page – subject.js
// JavaScript Document
define(function(require) {
'use strict';
var Observers = require('observer/observers'); //use require module to bring in observer.js we just created. Capitalize the object because it takes in a constructor
var Collection = function (items) { //the subject will hold of list of items the observer is subscribed to. Subject is based on array and observers are notified whenever an item is added or removed from this array
this.observers = new Observers(); //subject maintains list of observers
this.collection = items || []; //the array being observed is under property called collection, and is initalized as empty array
};
Collection.prototype.observe = function (observer) { //observe method receive observer to add
this.observers.add(observer); //and passes the observer to the array with the add method
};
Collection.prototype.unObserve = function (observer) {//unObserve method receive observer
this.observers.remove(observer); //and passes data to the remove method to be taken from the array
};
Collection.prototype.notify = function (event, data) { //this method will notify observer of a change in state. Receives event and data to pass to observers
this.observers.get().forEach(function (observer) { //inside method, must get the list of observers. Then hook into the array with forEach method and create callback that is passed each observer from array of observers
observer.notify(event, data); //inside callback we use notify method and pass through name of event, and the data
});
};
Collection.prototype.add = function (item) { //create subjects add method receive the new item to add to the array
this.collection.push(item); //then pushes the new item into the collection array
this.notify('added', item); //then invokes notify method and specifies the event name as added, then passes item that was added to collection
};
Collection.prototype.remove = function (itemToRemove) { //remove method for collection object
this.collection = this.collection.filter(function (item) { //receive the item to remove and uses filter method of override collection array
if (item !== itemToRemove) { //if the item is not the item to remove, returns true
return true;
}
this.notify('removed', item); //if it is the item to remove, we invoke notify method first and specify the event name as removed, then pass the item for removal
return false;
}, this);
};
return Collection; //return the constructor
});
Step 3: Page – observer.js
// JavaScript Document
define(function() {
'use strict';
var Observer = function (name) { //add simple observer class so we can create new observers. The constructor will accept a name property so we know which observer is which
this.name = name;
};
Observer.prototype.notify = function (event, data) { //observers will need a notify method
console.log('The event was ', '"' + event + '",', 'the data was', data, 'and I am ', this.name);//add log statement for example
};
return Observer;
});
Step 4: Page – main.js
// JavaScript Document
require (
['factory/init', 'pubSub/init', 'strategy/init', 'observer/init'],
function (factory, pubSub, strategy, observer) {
'use strict';
var examples = {
factory: factory,
pubSub: pubSub,
strategy: strategy,
observer: observer
};
window.runExample = function (example) {
examples[example].init();
};
}
);
Step 5: Page – init.js
define(function(require) {
'use strict';
return {
init: function() {
var subject, observer, otherObserver, data, moreData, //add variables
Subject = require('observer/subject'), //oad subject module
Observer = require('observer/observer'); //load observer module
subject = new Subject(); //instantiate a subject
observer = new Observer('observer1'); //instantiate observer
otherObserver = new Observer('observer2'); //initialize other observer
data = { //create data objects to pass into items being observed
prop: 'something'
};
moreData = {
prop: 'something else'
};
subject.observe(observer); //add our observers to observer collection
subject.observe(otherObserver);
subject.add(data); //add our data to the subjects collection
subject.add(moreData);
subject.unObserve(observer);
subject.remove(data);
}
};
});
In this situation, the observers will be coupled to the subjects. This isn’t inherently bad as some coupling is okay, and indeed, data in your applications will often necessarily be coupled. However, we want to avoid any unnecessary, unintentional or excessive coupling