--------
Augment Function so you can follow
/* Augment function, improved. */
function augment(receivingClass, givingClass) {
if(arguments[2]) { // Only give certain methods.
for(var i = 2, len = arguments.length; i < len; i++) {
receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
}
} else { // Give all methods.
for(methodName in givingClass.prototype) {
if(!receivingClass.prototype[methodName]) {
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
}
}
}
You can now write augment(Author, Mixin, 'serialize'); to only augment Author with
the single serialize method. More method names can be added if you want to augment with
more than one method.
Often it makes more sense to augment a class with a few methods than it does to make
one class inherit from another. This is a lightweight way to prevent code duplication. Unfortunately,
there aren’t many situations where it can be used. Only methods general enough to be
used in very dissimilar classes make good candidates for sharing (if the classes aren’t that dissimilar,
normal inheritance is often a better choice).
…end of augment part……
-------
We will repeat the example one more time using mixin classes. We will create one mixin class
with all of the methods we want to share. Then we will create a new class and use augment to
share those methods:
/* Mixin class for the edit-in-place methods. */
var EditInPlaceMixin = function() {}; //empty Constructor
EditInPlaceMixin.prototype = {
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;
}
};
The mixin class holds nothing but the methods. To create a functional class, make a constructor
and then call augment:
/* EditInPlaceField class. */
function EditInPlaceField(id, parent, value) {
this.id = id;
this.value = value || 'default value';
this.parentElement = parent;
this.createElements(this.id);
this.attachEvents();
};
augment(EditInPlaceField, EditInPlaceMixin); //AUGMENT CALL
You can now instantiate the class in the exact same way as with classical inheritance. To
create the class that uses a text area field, you will not subclass EditInPlaceField. Instead,
simply create a new class (with a constructor) and augment it from the same mixin class. But
before augmenting it, define a few methods. Since these are in place before augmenting it,
they will not get overridden:
/* EditInPlaceArea class. */
function EditInPlaceArea(id, parent, value) {
this.id = id;
this.value = value || 'default value';
this.parentElement = parent;
this.createElements(this.id);
this.attachEvents();
};
// Add certain methods so that augment won't include them.
EditInPlaceArea.prototype.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.prototype.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.prototype.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);
};
augment(EditInPlaceArea, EditInPlaceMixin); //AUGMENT CALL
The mixin technique works in this example, but not as well as the other two techniques.
In the end, the objects created by each of the techniques are almost identical, but from an
organizational standpoint, strict inheritance makes more sense than augmentation.
Mixin classes work well for methods that are shared between several disparate classes, but in this example, the mixin class is used to provide all of the methods, for two very similar classes.
Code maintenance would be easier with the first two examples because it is immediately obvious
where the methods came from and how the classes and objects were organized.
Sharing general-purpose methods that can act on all types of objects is a much better use
of mixin classes. Some examples of this are methods that serialize an object to a string representation, or output its state for debugging.
It is also possible to use mixin classes to emulate
enumerations or iterators, as found in some other object-oriented languages.
No comments:
Post a Comment