I think it is really important keep the code more general and flexible as possible; one rule, that i would like to introduce here, is to use the right level of abstraction when accessing the data structure elements, trying to centralize the read / write operations on them.
Many times i saw pieces of code that work directly on the data structure: that is a problem when that data structure, at some point, gets changed and then the developers are forced to manually update every line of code involved with it.
A real example: let think about implementing a piece code that meets this requirement:
Using jQuery and a table as target, clicking on a td of the table, we want to get it selected. When a td is selected, the click makes it deselected. The var tableSelector will be the selector to get the target table. Also, we want the selected elements stored into a list.
This is an implemention i don’t like:
// global (for the click's handler) selection // and selectedClass vars var selection = [], selectedClass = "selected"; $(tableSelector).click( function(e) { // i am not sure about nodeName as the best choice if( e.target.nodeName == "TD" ) { var i = 0, el = e.target; // when an index of selection refers to el, // the condition (i < selection.length ) is true for(; selection[i] != el; i++) {}; // selected state toggle if( i < selection.length ) { selection.push( el ); el.addClass( selectedClass ); } else { selection.splice(i, 1); el.removeClass( selectedClass ); } } });
Checking this code, the selection array is directly accessed: every where you use the selection, you assume it is an array (note: the real bad here is that the event handler has got some application logic, by accessing the data structure, but it helps the example so i don’t go through this point).
What to do if, at some point, you need to make an hash map of it? You will get to check every line of code involved with it and changing them, like that:
// global (for the click's handler) selection // and selectedClass vars var selection = {}, selectedClass = "selected"; $(tableSelector).click( function(e) { // i am not sure about nodeName as the best choice if( e.target.nodeName == "TD"; ) { var i, el = e.target, isSelected = false; for( i in selection[i] ) { if( i == el ) { isSelected = true; break; } } // selected state toggle // i am assuming that el // has got an id but it is // not obvious if( !isSelected ) { selection[ el.id ] = el; el.addClass( selectedClass ); } else { delete selection[ el.id ]; el.removeClass( selectedClass ); } } });
In the example there are only few lines to be changed but, in a real work enviroment, probably lines will be many and in different places.
How to avoid this situation? What i like to do is to centralize the data structure access, like that:
// you would like to add COMMENTS for the // class, contructor and every member! var Selection = function() { this._selection = []; } Selection.prototype.select = function(el) { if( !this.has(el) ) { this._selection.push( el ); return true; } }; Selection.prototype.deselect = function(el) { if( this.has(el) ) { this._selection.splice(i, 1); } }; Selection.prototype.has = function( el ) { // you would like to add conditions // to check that el is a DOM element if( !el ) { return; } var i = 0; for(; this._selection[i] != el; i++) {}; return i < this._selection.length; }; Selection.prototype.length = function () { return this._selection.length; }
Now our click handler appears like this:
// global (for the click's handler) selection // and selectedClass vars var selection = new Selection(), selectedClass = "selected"; $(tableSelector).click( function(e) { var el = e.target; if( selection.toggle(el) ){ el.addClass( selectedClass ); } else { el.removeClass( selectedClass ); } });
We have delegated the access, to the data structure, to the Selection object: by piloting that object, we can write (and read) the data.
More, add changes to the data structure doesn’t involve the code in which we are using the selection.
I hope that can be helpful, thanks for reading!