Friday, April 23, 2010

Inheritance - Example - Prototypal Inheritance

Using Prototypal Inheritance

Despite the fact that classical and prototypal inheritance are fundamentally different, repeating the exercise using prototypal inheritance really shows how similar the end code can be
between the two:

/* EditInPlaceField object. */

var EditInPlaceField = {
 configure: function(id, parent, value) {
  this.id = id;
  this.value = value || 'default value';
  this.parentElement = parent;
  this.createElements(this.id);
  this.attachEvents();
 },
 createElements: function(id) {
  this.containerElement = document.createElement('div');
  this.parentElement.appendChild(this.containerElement);
  
  this.staticElement = document.createElement('span');
  this.containerElement.appendChild(this.staticElement);
  this.staticElement.innerHTML = this.value;
  
  this.fieldElement = document.createElement('input');
  this.fieldElement.type = 'text';
  this.fieldElement.value = this.value;
  this.containerElement.appendChild(this.fieldElement);
  
  this.saveButton = document.createElement('input');
  this.saveButton.type = 'button';
  this.saveButton.value = 'Save';
  this.containerElement.appendChild(this.saveButton);
  
  this.cancelButton = document.createElement('input');
  this.cancelButton.type = 'button';
  this.cancelButton.value = 'Cancel';
  this.containerElement.appendChild(this.cancelButton);
  
  this.convertToText();
 },
 attachEvents: function() {
  var that = this;
  addEvent(this.staticElement, 'click', function() { that.convertToEditable(); });
  addEvent(this.saveButton, 'click', function() { that.save(); });
  addEvent(this.cancelButton, 'click', function() { that.cancel(); });
 },
 convertToEditable: function() {
  this.staticElement.style.display = 'none';
  this.fieldElement.style.display = 'inline';
  this.saveButton.style.display = 'inline';
  this.cancelButton.style.display = 'inline';
  this.setValue(this.value);
 },
 save: function() {
  this.value = this.getValue();
  var that = this;
  var callback = {
   success: function() { that.convertToText(); },
   failure: function() { alert('Error saving value.'); }
   };
  ajaxRequest('GET', 'save.php?id=' + this.id + '&value=' + this.value, callback);
 },
 cancel: function() {
  this.convertToText();
 },
 convertToText: function() {
  this.fieldElement.style.display = 'none';
  this.saveButton.style.display = 'none';
  this.cancelButton.style.display = 'none';
  this.staticElement.style.display = 'inline';
  this.setValue(this.value);
 },
 setValue: function(value) {
  this.fieldElement.value = value;
  this.staticElement.innerHTML = value;
 },
 getValue: function() {
  return this.fieldElement.value;
 }
};


Instead of a class, there is now an object.
Prototypal inheritance doesn’t use constructors,
so you move that code into a configure method instead. Other than that, the code is almost
identical to the first example.

Creating new objects from this EditInPlaceField prototype object looks very different from instantiating a class:
var titlePrototypal = clone(EditInPlaceField);
titlePrototypal.configure(' titlePrototypal ', $('doc'), 'Title Here');
var currentTitleText = titlePrototypal.getValue();

Instead of using the new operator, use the clone function to create a copy.
Then configure that copy.
At this point you can interact with the object titlePrototypal in the same way as
you would with the previous titleClassical object.
The two objects are almost indistinguishable and can be managed using the same API.

Creating a child object from this one also uses the clone function:
/* EditInPlaceArea object. */


var EditInPlaceArea = clone(EditInPlaceField);


// Override certain methods.
EditInPlaceArea.createElements = function(id) {
 this.containerElement = document.createElement('div');
 this.parentElement.appendChild(this.containerElement);
 
 this.staticElement = document.createElement('p');
 this.containerElement.appendChild(this.staticElement);
 this.staticElement.innerHTML = this.value;
 
 this.fieldElement = document.createElement('textarea');
 this.fieldElement.value = this.value;
 this.containerElement.appendChild(this.fieldElement);
 
 this.saveButton = document.createElement('input');
 this.saveButton.type = 'button';
 this.saveButton.value = 'Save';
 this.containerElement.appendChild(this.saveButton);
 
 this.cancelButton = document.createElement('input');
 this.cancelButton.type = 'button';
 this.cancelButton.value = 'Cancel';
 this.containerElement.appendChild(this.cancelButton);
 
 this.convertToText();
};

EditInPlaceArea.convertToEditable = function() {
 this.staticElement.style.display = 'none';
 this.fieldElement.style.display = 'block';
 this.saveButton.style.display = 'inline';
 this.cancelButton.style.display = 'inline';
 
 this.setValue(this.value);
};
EditInPlaceArea.convertToText = function() {
 this.fieldElement.style.display = 'none';
 this.saveButton.style.display = 'none';
 this.cancelButton.style.display = 'none';
 this.staticElement.style.display = 'block';
 
 this.setValue(this.value);
};



You simply create a copy of the EditInPlaceField object, and then overwrite some of the
methods.
This prototype object can be used and cloned in the same way as the first one can.
In fact, new prototype objects can be created in the same way, by cloning this one and making
a few changes.
Prototypal inheritance also seems ideal for this example, for the same reasons that classical
inheritance worked so well.
The only differences between the two are the way the class/object
is set up, and the way a new sub-object/instance is created.

Most of the code (including all of the methods) is completely unchanged. This illustrates how easily you can convert from one
paradigm to the other.

It isn’t always this easy, especially with classes and objects that make
extensive use of arrays or objects as members, but for the most part you need only modify
a bit of the syntax.

Using prototypal inheritance in this example doesn’t really provide anything over classical
inheritance.
The objects do not use many default values, so you aren’t really saving any memory.

Personally, we would have a hard time picking one paradigm over the other in this example;
both work equally well.

No comments:

Post a Comment