Created
April 30, 2009 18:06
-
-
Save furf/104581 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Abstract methods and interfaces | |
/** | |
* AbstractMethod factory class | |
* | |
* @method AbstractMethod | |
* @param {string} methodName Name of the method | |
* @return {type} Return value description | |
*/ | |
var AbstractMethod = function(methodName) { | |
// Return a function which throws an error indicating | |
// that it has not been overridden properly | |
return function() { | |
throw new Error(methodName + ' is an abstact method and cannot be called directly.'); | |
}; | |
}; | |
/** | |
* Method description | |
* | |
* @method AbstractClass | |
* @param {type} paramName Parameter description | |
* @param {type} paramName Parameter description | |
* @return {type} Return value description | |
*/ | |
var AbstractClass = function(name /*, interface */) { | |
if (typeof name !== 'string') { | |
throw new Error('AbstractClass expects the first argument to be a string.'); | |
} | |
var constructor = function() { | |
throw new Error(name + ' is an abstract class which cannot be instantiated directly.'); | |
}; | |
// Implement | |
if (arguments.length > 1) { | |
for (var i = arguments.length - 1; i >= 1; --i) { | |
var interface = arguments[i]; | |
if (!(interface instanceof Interface)) { | |
throw new Error('AbstractClass expects arguments two and above to be instances of the Interface class.'); | |
} | |
Interface.implement(constructor, interface); | |
} | |
} | |
return constructor; | |
}; | |
/** | |
* Interface class - returns an Interface object containing AbstractMethods | |
* | |
* @method Interface | |
* @param {string} name Name of interface | |
* @param {string|array} method Method name or array of method names to add to interface | |
*/ | |
var Interface = function(name, method) { | |
if (arguments.length !== 2) { | |
throw new Error('Interface expects exactly 2 arguments.'); | |
} | |
this.name = name; | |
this.methods = {}; | |
var methods = (method instanceof Array) ? method : [method]; | |
for (var i = 0, len = methods.length; i < len; ++i) { | |
var method = methods[i]; | |
if (typeof method !== 'string') { | |
throw new Error('Interface expects method names to be strings.'); | |
} | |
this.methods[method] = AbstractMethod(method); | |
} | |
}; | |
/** | |
* Implements an Interface to a class by adding AbstractMethods | |
* which need to be overridden | |
* | |
* @method implement | |
* @static | |
* @param {function} constructor The constructor function of a class | |
* @return {Interface|Array} interface An Interface or array of Interfaces to implement | |
*/ | |
Interface.implement = function(constructor, interface) { | |
// Make sure that all of the arguments are provided | |
if (arguments.length !== 2) { | |
throw new Error('Interface.implement requires exactly two arguments.') | |
} | |
// Make sure the first argument is a constructor function | |
if (typeof constructor !== 'function') { | |
throw new Error('Interface.implement requires the first argument to be a constructor function.'); | |
} | |
var interfaces = (interface instanceof Array) ? interface : [interface]; | |
// Iterate through all of the interface arguments | |
for (var i = 0, len = interfaces.length; i < len; ++i) { | |
var interface = interfaces[i]; | |
// Make sure each interface is an instance of Interface | |
if (interface.constructor !== Interface) { | |
throw new Error('Interface.implement expects arguments two and above to be instances of the Interface class.'); | |
} | |
// Assign each AbstractMethod of the Interface | |
// to the constructor's prototype if it is not | |
// already present in the prototype | |
for (method in interface.methods) { | |
if (typeof constructor.prototype[method] === 'undefined') { | |
constructor.prototype[method] = interface.methods[method]; | |
} | |
} | |
} | |
}; | |
/** | |
* Ensures that an object has properly implemented the methods of a given interface | |
* | |
* @method assert | |
* @static | |
* @param {object} object The object to validate | |
* @return {Interface|Array} interface An Interface or array of Interfaces to validate | |
* @return {boolean} true if all Interface methods are present in the object | |
*/ | |
Interface.assert = function(object, interface) { | |
// Make sure that all of the arguments are provided | |
if (arguments.length !== 2) { | |
throw new Error('Interface.assert requires exactly two arguments.') | |
} | |
var interfaces = (interface instanceof Array) ? interface : [interface]; | |
// Iterate through all of the interface arguments | |
for (var i = 1, len = interfaces.length; i < len; ++i) { | |
var interface = interfaces[i]; | |
// Make sure each argument is an instance of Interface | |
if (interface.constructor !== Interface) { | |
throw new Error('Interface.assert expects arguments two and above to be instances of the Interface class.'); | |
} | |
// Iterate through all of the methods of the interface | |
// to ensure they have been properly implemented | |
for (method in interface.methods) { | |
// Interface method is undefined | |
if (typeof object[method] === 'undefined') { | |
throw new Error('Object does not implement the ' + interface.name + ' interface. Method ' + method + ' is undefined.') | |
} | |
// Interface method matches the AbstractMethod of the | |
// interface and has therefore not been implemented | |
else if(object[method] === interface.methods[method]) { | |
throw new Error('Object does not implement the ' + interface.name + ' interface. Abstract method ' + method + ' has not been overridden.') | |
} | |
} | |
} | |
return true; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment