Skip to content

Instantly share code, notes, and snippets.

@furf
Created April 30, 2009 18:06
Show Gist options
  • Save furf/104581 to your computer and use it in GitHub Desktop.
Save furf/104581 to your computer and use it in GitHub Desktop.
// 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