I have seen the pattern implemented in different ways, but the idea is simple:
- You have some class - your interface - that simply specifies the names of some functions. (You may want to have a class called Interface that your actual interfaces instantiate, just so your interfaces are of type Interface)
- You then have some other class that implements such interface. This means that this second class must have at least all of the functions specified by the interface.
- Finally, you have some other function somewhere else, that expects to receive an object that implements the interface. In the sample code you mention, this function is addForm, which expects an object that implements the 'Composite' and 'FormItem' interfaces.
- This function then loops through all the methods of the interface(s) it expects, and checks that the object you passed in to it also have those methods. If a method from one of the interfaces is not found in the object passed into the function, it determines that object doesn't implement the interface, and throws an exception.
Some people may find this pattern unpractical because of the overhead involved, but given Javascript's lack of native support for interfaces, this is not too bad a solution. Some people may also find that using interfaces for small projects in Javascript is overkill.
Example
var Interface = function(name, methods) {
this.name = name;
this.methods = [];
if (methods.constructor == Array)
this.methods = methods;
else if (methods.constructor == String)
this.methods[0] = methods;
else
throw new Error("Interface must define methods as a String or an Array of Strings");
};
var InterfaceHelper = {
ensureImplements : function(obj, interfaces) {
// If interfaces is not an array, assume it's a function pointer
var toImplement = interfaces.constructor == Array ? interfaces : [interfaces];
var interface;
// For every interface that obj must implement:
for (var i = 0, len = toImplement.length; i < len; i++) {
interface = toImplement[i];
// Make sure it indeed is an interface
if (interface.constructor != Interface)
throw new Error("Object trying to implement a non-interface. "
+ interface.name + " is not an Interface.");
// Make sure obj has all of the methods described in the interface
for (var j = 0, interfaceLen = interface.methods.length; j < interfaceLen; j++)
if (!obj[interface.methods[j]])
throw new Error("Interface method not implemented. "
+ interface.name + " defines method " + interface.methods[j]);
}
return true;
}
};
var Drawable = new Interface("Drawable", ["onDraw"]);
var Surface = function() {
this.implements = ["Drawable"];
this.onDraw = function() {
console.log("Surface Drawing");
};
};
Usage
var myDrawableSurface = new Surface();
// Returns true
InterfaceHelper.ensureImplements(myDrawableSurface, Drawable);
// Returns false (Error thrown)
InterfaceHelper.ensureImplements(myDrawableSurface, Array);