/* The Bicycle interface. */
var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair',
'getPrice']);
/* The AcmeComfortCruiser class. */
var AcmeComfortCruiser = function() { // implements Bicycle
...
};
AcmeComfortCruiser.prototype = {
assemble: function() {
...
},
wash: function() {
...
},
ride: function() {
...
},
repair: function() {
...
},
getPrice: function() {
return 399.00;
}
};
/* The BicycleDecorator abstract decorator class. */
var BicycleDecorator = function(bicycle) { // implements Bicycle
Interface.ensureImplements(bicycle, Bicycle);
this.bicycle = bicycle;
}
BicycleDecorator.prototype = {
assemble: function() {
return this.bicycle.assemble();
},
wash: function() {
return this.bicycle.wash();
},
ride: function() {
return this.bicycle.ride();
},
repair: function() {
return this.bicycle.repair();
},
getPrice: function() {
return this.bicycle.getPrice();
}
};
/* HeadlightDecorator class. */
var HeadlightDecorator = function(bicycle) { // implements Bicycle
this.superclass.constructor(bicycle); // Call the superclass's constructor.
}
extend(HeadlightDecorator, BicycleDecorator); // Extend the superclass.
HeadlightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach headlight to handlebars.';
};
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
};
/* TaillightDecorator class. */
var TaillightDecorator = function(bicycle) { // implements Bicycle
this.superclass.constructor(bicycle); // Call the superclass's constructor.
}
extend(TaillightDecorator, BicycleDecorator); // Extend the superclass.
TaillightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach taillight to the seat post.';
};
TaillightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 9.00;
};
/* Usage. */
var myBicycle = new AcmeComfortCruiser(); // Instantiate the bicycle.
alert(myBicycle.getPrice()); // Returns 399.00
myBicycle = new TaillightDecorator(myBicycle); // Decorate the bicycle object
// with a taillight.
alert(myBicycle.getPrice()); // Now returns 408.00
myBicycle = new HeadlightDecorator(myBicycle); // Decorate the bicycle object
// again, now with a headlight.
alert(myBicycle.getPrice()); // Now returns 423.00
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
};
var myBicycle = new AcmeComfortCruiser(); // Instantiate the bicycle.
alert(myBicycle.getPrice()); // Returns 399.00
myBicycle = new HeadlightDecorator(myBicycle); // Decorate the bicycle object
// with the first headlight.
myBicycle = new HeadlightDecorator(myBicycle); // Decorate the bicycle object
// with the second headlight.
myBicycle = new TaillightDecorator(myBicycle); // Decorate the bicycle object
// with a taillight.
alert(myBicycle.getPrice()); // Now returns 438.00
/* FrameColorDecorator class. */
var FrameColorDecorator = function(bicycle, frameColor) { // implements Bicycle
this.superclass.constructor(bicycle); // Call the superclass's constructor.
this.frameColor = frameColor;
}
extend(FrameColorDecorator, BicycleDecorator); // Extend the superclass.
FrameColorDecorator.prototype.assemble = function() {
return 'Paint the frame ' + this.frameColor + ' and allow it to dry. ' +
this.bicycle.assemble();
};
FrameColorDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 30.00;
};
var myBicycle = new AcmeComfortCruiser(); // Instantiate the bicycle.
myBicycle = new FrameColorDecorator(myBicycle, 'red'); // Decorate the bicycle
// object with the frame color.
myBicycle = new HeadlightDecorator(myBicycle); // Decorate the bicycle object
// with the first headlight.
myBicycle = new HeadlightDecorator(myBicycle); // Decorate the bicycle object
// with the second headlight.
myBicycle = new TaillightDecorator(myBicycle); // Decorate the bicycle object
// with a taillight.
alert(myBicycle.assemble());
/* Returns:
"Paint the frame red and allow it to dry. (Full instructions for assembling
the bike itself go here) Attach headlight to handlebars. Attach headlight
to handlebars. Attach taillight to the seat post."
*/
/* LifetimeWarrantyDecorator class. */
var LifetimeWarrantyDecorator = function(bicycle) { // implements Bicycle
this.superclass.constructor(bicycle); // Call the superclass's constructor.
}
extend(LifetimeWarrantyDecorator, BicycleDecorator); // Extend the superclass.
LifetimeWarrantyDecorator.prototype.repair = function() {
return 'This bicycle is covered by a lifetime warranty. Please take it to ' +
'an authorized Acme Repair Center.';
};
LifetimeWarrantyDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 199.00;
};
/* TimedWarrantyDecorator class. */
var TimedWarrantyDecorator = function(bicycle, coverageLengthInYears) {
// implements Bicycle
this.superclass.constructor(bicycle); // Call the superclass's constructor.
this.coverageLength = coverageLengthInYears;
this.expDate = new Date();
var coverageLengthInMs = this.coverageLength * 365 * 24 * 60 * 60 * 1000;
expDate.setTime(expDate.getTime() + coverageLengthInMs);
}
extend(TimedWarrantyDecorator, BicycleDecorator); // Extend the superclass.
TimedWarrantyDecorator.prototype.repair = function() {
var repairInstructions;
var currentDate = new Date();
if(currentDate < expDate) {
repairInstructions = 'This bicycle is currently covered by a warranty. ' +
'Please take it to an authorized Acme Repair Center.';
}
else {
repairInstructions = this.bicycle.repair();
}
return repairInstructions;
};
TimedWarrantyDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + (40.00 * this.coverageLength);
};
/* BellDecorator class. */
var BellDecorator = function(bicycle) { // implements Bicycle
this.superclass.constructor(bicycle); // Call the superclass's constrcutor.
}
extend(BellDecorator, BicycleDecorator); // Extend the superclass.
BellDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach bell to handlebars.';
};
BellDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 6.00;
};
BellDecorator.prototype.ringBell = function() {
return 'Bell rung.';
};
var myBicycle = new AcmeComfortCruiser(); // Instantiate the bicycle.
myBicycle = new BellDecorator(myBicycle); // Decorate the bicycle object
// with a bell.
alert(myBicycle.ringBell()); // Returns 'Bell rung.'
var myBicycle = new AcmeComfortCruiser(); // Instantiate the bicycle.
myBicycle = new BellDecorator(myBicycle); // Decorate the bicycle object
// with a bell.
myBicycle = new HeadlightDecorator(myBicycle); // Decorate the bicycle object
// with a headlight.
alert(myBicycle.ringBell()); // Method not found.
/* The BicycleDecorator abstract decorator class, improved. */
var BicycleDecorator = function(bicycle) { // implements Bicycle
this.bicycle = bicycle;
this.interface = Bicycle;
// Loop through all of the attributes of this.bicycle and create pass-through
// methods for any methods that aren't currently implemented.
outerloop: for(var key in this.bicycle) {
// Ensure that the property is a function.
if(typeof this.bicycle[key] !== 'function') {
continue outerloop;
}
// Ensure that the method isn't in the interface.
for(var i = 0, len = this.interface.methods.length; i < len; i++) {
if(key === this.interface.methods[i]) {
continue outerloop;
}
}
// Add the new method.
var that = this;
(function(methodName) {
that[methodName] = function() {
return that.bicycle[methodName]();
};
})(key);
}
}
BicycleDecorator.prototype = {
assemble: function() {
return this.bicycle.assemble();
},
wash: function() {
return this.bicycle.wash();
},
ride: function() {
return this.bicycle.ride();
},
repair: function() {
return this.bicycle.repair();
},
getPrice: function() {
return this.bicycle.getPrice();
}
};
var AcmeBicycleShop = function() {};
extend(AcmeBicycleShop, BicycleShop);
AcmeBicycleShop.prototype.createBicycle = function(model) {
var bicycle;
switch(model) {
case 'The Speedster':
bicycle = new AcmeSpeedster();
break;
case 'The Lowrider':
bicycle = new AcmeLowrider();
break;
case 'The Flatlander':
bicycle = new AcmeFlatlander();
break;
case 'The Comfort Cruiser':
default:
bicycle = new AcmeComfortCruiser();
}
Interface.ensureImplements(bicycle, Bicycle);
return bicycle;
};
/* AcmeBicycleShop factory class, with decorators. */
var AcmeBicycleShop = function() {};
extend(AcmeBicycleShop, BicycleShop);
AcmeBicycleShop.prototype.createBicycle = function(model, options) {
// Instantiate the bicycle object.
var bicycle = new AcmeBicycleShop.models[model]();
// Iterate through the options and instantiate decorators.
for(var i = 0, len = options.length; i < len; i++) {
var decorator = AcmeBicycleShop.options[options[i].name];
if(typeof decorator !== 'function') {
throw new Error('Decorator ' + options[i].name + ' not found.');
}
var argument = options[i].arg;
bicycle = new decorator(bicycle, argument);
}
// Check the interface and return the finished object.
Interface.ensureImplements(bicycle, Bicycle);
return bicycle;
};
// Model name to class name mapping.
AcmeBicycleShop.models = {
'The Speedster': AcmeSpeedster,
'The Lowrider': AcmeLowrider,
'The Flatlander': AcmeFlatlander,
'The Comfort Cruiser': AcmeComfortCruiser
};
// Option name to decorator class name mapping.
AcmeBicycleShop.options = {
'headlight': HeadlightDecorator,
'taillight': TaillightDecorator,
'bell': BellDecorator,
'basket': BasketDecorator,
'color': FrameColorDecorator,
'lifetime warranty': LifetimeWarrantyDecorator,
'timed warranty': TimedWarrantyDecorator
};
var myBicycle = new AcmeSpeedster();
myBicycle = new FrameColorDecorator(myBicycle, 'blue');
myBicycle = new HeadlightDecorator(myBicycle);
myBicycle = new TaillightDecorator(myBicycle);
myBicycle = new TimedWarrantyDecorator(myBicycle, 2);
var alecsCruisers = new AcmeBicycleShop();
var myBicycle = alecsCruisers.createBicycle('The Speedster', [
{ name: 'color', arg: 'blue' },
{ name: 'headlight' },
{ name: 'taillight' },
{ name: 'timed warranty', arg: 2 }
]);
==================================================
Method Profiler
/* ListBuilder class. */
var ListBuilder = function(parent, listLength) {
this.parentEl = $(parent);
this.listLength = listLength;
};
ListBuilder.prototype = {
buildList: function() {
var list = document.createElement('ol');
this.parentEl.appendChild(list);
for(var i = 0; i < this.listLength; i++) {
var item = document.createElement('li');
list.appendChild(item);
}
}
};
/* SimpleProfiler class. */
var SimpleProfiler = function(component) {
this.component = component;
};
SimpleProfiler.prototype = {
buildList: function() {
var startTime = new Date();
this.component.buildList();
var elapsedTime = (new Date()).getTime() - startTime.getTime();
console.log('buildList: ' + elapsedTime + ' ms');
}
};
/* Usage. */
var list = new ListBuilder('list-container', 5000); // Instantiate the object.
list = new SimpleProfiler(list); // Wrap the object in the decorator.
list.buildList(); // Creates the list and displays "buildList: 298 ms".
/* MethodProfiler class. */
var MethodProfiler = function(component) {
this.component = component;
this.timers = {};
for(var key in this.component) {
// Ensure that the property is a function.
if(typeof this.component[key] !== 'function') {
continue;
}
// Add the method.
var that = this;
(function(methodName) {
that[methodName] = function() {
that.startTimer(methodName);
var returnValue = that.component[methodName].apply(that.component,
arguments);
that.displayTime(methodName, that.getElapsedTime(methodName));
return returnValue;
};
})(key); }
};
MethodProfiler.prototype = {
startTimer: function(methodName) {
this.timers[methodName] = (new Date()).getTime();
},
getElapsedTime: function(methodName) {
return (new Date()).getTime() - this.timers[methodName];
},
displayTime: function(methodName, time) {
console.log(methodName + ': ' + time + ' ms');
}
};
/* Usage. */
var list = new ListBuilder('list-container', 5000);
list = new MethodProfiler(list);
list.buildList('ol'); // Displays "buildList: 301 ms".
list.buildList('ul'); // Displays "buildList: 287 ms".
list.removeLists('ul'); // Displays "removeLists: 10 ms".
list.removeLists('ol'); // Displays "removeLists: 12 ms".