vendredi 27 mars 2015

JavaScript: Why so much abstraction/interfacing in Addy's Observer Pattern?

I'm working through the design pattern example for the Observer Pattern in Addy Osmani's book, "JavaScript Design Patterns". My question is why is it important that there are so many levels of abstraction in his implementation of the pattern?


For instance, in his example, just to add an observer ('push' an observer to an array), this involves:



  • using the native push() method.

  • creating a ObjectList.add() method.

  • inheriting/extending the ObjectList object with the Subject object.

  • creating the Subject.addObserver() method which to be used as an interface, but which uses the ObjectList.add method under the hood.

  • extending the Subject.addObserver() method for new objects.

  • Implementing it by calling the addObserver() method on the newly extended object.


Here's the code example for the design pattern in its entirety:



function ObserverList(){
this.observerList = [];
}

ObserverList.prototype.add = function( obj ){
return this.observerList.push( obj );
};

ObserverList.prototype.count = function(){
return this.observerList.length;
};

ObserverList.prototype.get = function( index ){
if( index > -1 && index < this.observerList.length ){
return this.observerList[ index ];
}
};

ObserverList.prototype.indexOf = function( obj, startIndex ){
var i = startIndex;

while( i < this.observerList.length ){
if( this.observerList[i] === obj ){
return i;
}
i++;
}

return -1;
};

ObserverList.prototype.removeAt = function( index ){
this.observerList.splice( index, 1 );
};

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

Subject.prototype.addObserver = function( observer ){
this.observers.add( observer );
};

Subject.prototype.removeObserver = function( observer ){
this.observers.removeAt( this.observers.indexOf( observer, 0 ) );
};

Subject.prototype.notify = function( context ){
var observerCount = this.observers.count();
for(var i=0; i < observerCount; i++){
this.observers.get(i).update( context );
}
};

// The Observer
function Observer(){
this.update = function(){
// ...
};
}


And here's the implementation/usage:


HTML



<button id="addNewObserver">Add New Observer checkbox</button>
<input id="mainCheckbox" type="checkbox"/>
<div id="observersContainer"></div>


Script



// Extend an object with an extension
function extend( extension, obj ){
for ( var key in extension ){
obj[key] = extension[key];
}
}

// References to our DOM elements

var controlCheckbox = document.getElementById( "mainCheckbox" ),
addBtn = document.getElementById( "addNewObserver" ),
container = document.getElementById( "observersContainer" );


// Concrete Subject

// Extend the controlling checkbox with the Subject class
extend( new Subject(), controlCheckbox );

// Clicking the checkbox will trigger notifications to its observers
controlCheckbox.onclick = function(){
controlCheckbox.notify( controlCheckbox.checked );
};

addBtn.onclick = addNewObserver;

// Concrete Observer

function addNewObserver(){

// Create a new checkbox to be added
var check = document.createElement( "input" );
check.type = "checkbox";

// Extend the checkbox with the Observer class
extend( new Observer(), check );

// Override with custom update behaviour
check.update = function( value ){
this.checked = value;
};

// Add the new observer to our list of observers
// for our main subject
controlCheckbox.addObserver( check );

// Append the item to the container
container.appendChild( check );
}


Now I compared his implementation with other implementations of the same pattern (books and blogs). And it seems that Addy adds a ton more abstraction than other implementors of the observer pattern. The question is, why? Couldn't this be done more simple? Does this achieve a greater degree of decoupling? If so, how exactly is that? Doesn't the design pattern itself create a decoupling?


Aucun commentaire:

Enregistrer un commentaire