Tuesday, October 14, 2008

Interface in Javascript

While there are other suggested ways to emulate Interface in JavaScript, I prefer the method I demonstrate (below) because of it's simplicity. If you really want strict enforcement, you'll need a system that implements decorators (see Decorator Pattern) which may provide strict[er] enforcement at run-time but ends up looking a little less than elegant (note the sarcasm).

For my purposes, I just want to assist development of complex JavaScript solutions by reducing complexity and avoiding inadvertent mistakes. This is not a solution that ensures strict enforcement nor are any of the solutions I put forth in writing JavaScript in an OO manner.

In OO terms, an Interface is a contract by which the implementing class (or in the case of JavaScript, a function) adheres. An Interface describes a desired behavior and enforces adherence for any Class which implement the Interface. Implementing interfaces, again in OO terms, is like saying OneClass 'IS LIKE A' NutherClass, or more correctly 'PROMISES TO BEHAVE LIKE', in contrast to Inheritance where you might say that a DerivedClass 'IS A' BaseClass.

As JavaScript applications are becoming increasingly complex with multiple team members participating in the design and construction process, we need a means to enforce the Interface contract beyond just commenting the JavaScript code. The Interface is common in many design patterns and, while we can omit it's use, the consequences are that the programmer[s] must remember all instances where they intend to implement a grouping of functionality or behavior.

Since the Interface is not available in JavaScript, we are forced to emulate it in the most elegant and functional manner we can through JavaScript (read as 'duck typing'). Here for your benefit is my take on a simple way to emulate the Interface in javascript:


function Implements(implementer,pseudoInterface)
{
for (prop in pseudo)
for (prop in pseudoInterface)
if (typeof pseudoInterface[prop] === "function" )
if (typeof implementer[prop] != "function")
throw new Implements.FunctionNotImplemented(implementer.constructor.name,prop,pseudoInterface.constructor.name);
}

Implements.FunctionNotImplemented = function(implementing,implemented,pseudoInterface){
this.implementing = implementing;
this.implemented = implemented;
this.pseudoInterface = pseudoInterface;
};
Implements.FunctionNotImplemented.prototype.message = function(){
return this.implementing + " does not implement function: " + this.implemented + " as contracted in psuedo-interface: " + this.pseudoInterface;
};

function IUpdateable(){}
IUpdateable.prototype.update = function(){};
IUpdateable.prototype.sendupdate = function(){};

function myClass()
{ Implements(this,new IUpdateable());
}
myClass.prototype.update = function(){ alert("this object had it's update method called"); }

function main()
{
try { var myobj = new myClass(); }
catch (e if e instanceof Implements.FunctionNotImplemented) alert(e.message());
catch (e) alert("cannot handle exception: " + e.String()); // log error
}

Notice that it is only at instantiation of the object implementing the interface that a check is performed. You may very well be running code and never see your error unless the offending implementer is instantiated and fails to implement the methods defined in the interface. If we attach our logic to the prototype of Function, we can resolve this problem and also do away with messy global declarations. While we're at it, I'm going to add the ability to implement multiple Interfaces:

Function.prototype._Implements = function(pseudoInterface,opt_options){
for (prop in pseudoInterface.prototype)
if (typeof pseudoInterface.prototype[prop] === "function")
if (typeof this.prototype[prop] != "function")
throw new Function.MethodNotImplemented(this.name,prop,pseudoInterface.name);
};

Function.prototype._ImplementsArray = function(interfaces,opt_options){
if (interfaces instanceof Array)
for (item in interfaces.reverse())
if (typeof interfaces[item] === "function")
this._Implements(interfaces[item],opt_options);
else throw "The Array supplied contains an item that is not a Function";
else throw "The parameter supplied is not an Array";
};

Function.prototype.Implements = function(interfaces,opt_options){
try {
if (interfaces instanceof Array) this._ImplementsArray(interfaces,opt_options);
else if (typeof interfaces === "function") this._Implements(interfaces,opt_options);
else throw "The parameter 'interfaces' supplied was not an Array or Function";
}
catch (e)
{ alert(e.toString()); }
};

That is all well and good but you may have noticed that the implementing object bares no relation to the implemented interface. In other languages, like Java or C#, we can perform a type test to ask if an object IS of a type it has implemented which will result in true. In this way, implementing interfaces behaves like multiple inheritance. Our solution above will not respond the same way with JavaScript instanceof. Because the prototype chain cannot be branched (it is a one-to-one, child-to-parent relationship), our Interface solution appears more like a pseudo-Interface.

Of course, we could insert the Interface(s) at the top of the prototype chain but if our implementing class(es) were derived from classes not implementing the interface, errors would be thrown and brittleness introduced into the solution. This would become, certainly, more of an issue were one deriving from classes in an external package (such as the Google Maps API) where we have no (classical) means to ensure due diligence in regards to the contract defined by an Interface.

Yes, JavaScript is unique in it's flexibility and we can simply add methods (properties with a value of a function) to the prototype of the, for example, GMap2 function. But that is contrary to legibility where, in the case of Google Maps API and other external API's, the package is opaque (through obfuscation, compression, and in some cases encrypted) aside from the public documentation.

Again, JavaScript is unique in this ability but the well-rounded programmer uses several languages that when aggregated, can and should be composed complying (where possible) to a common or similar form within OO style.

Yet I'm still unsatisfied with our inability to use instanceof in detecting a class that implements a given Interface. The solution is to write our own instanceof and attach it to the prototype of Object:


Object.prototype.isInstanceOf = function(pseudoInterface){
if (this instanceof pseudoInterface) return true;
else
{
try { this.constructor.Implements(pseudoInterface); return true; }
catch (e) { return false; }
}
};


And there you have it, a not-too brittle means of implementing interfaces in JavaScript.

6 comments:

  1. Great!!!!
    bug found:

    "for (prop in pseudo)"

    should be

    "for (prop in pseudoInterface)"

    ReplyDelete
  2. Hey Bernie,

    Thanks a bunch for the correction :)

    ReplyDelete
  3. Good tutorial... But that means if you try to implement EventListener to create a custom events - it is not possible! ffs

    ReplyDelete
  4. This seems useful except that it appears to remain brittle in the event of iterating through a literal object containing known types. For example:

    var list = {
    one : "one",
    two : "two",
    three : "three"
    };

    for(var i in list) {
    if(list[i].matches("one");
    }

    // Adding isInstanceOf to Object.prototype causes an uncaught TypeError: Object function(pseudoInterface) {...} has no method match when i == "isInstanceOf"

    ReplyDelete
  5. Rein, it looked neat but when I implemented and used Object.prototype.isInstanceOf suddenly I started seeing that function's text showing up in my HTML! Isn't messing with Object.prototype a Bad thing?


    http://stackoverflow.com/questions/6877005/extending-object-prototype-javascript

    http://erik.eae.net/archives/2005/06/06/22.13.54/ "Object.prototype is verboten"

    Cheers,
    Michael

    ReplyDelete
  6. Hi Michael,

    Thanks for contributing. I saw from the links there are some strong opinions on the subject. I lean towards freedom to do what seems natural with the language and within the boundaries that the designers of Javascript have given us. In which case, I refer to Crockfor: http://javascript.crockford.com/code.html#for%20statement - you will need to use hasOwnProperty() to filter out properties added to the prototype... Sorry my code messed up your code ;)

    ReplyDelete