Thursday, August 25, 2011

Yet another way programming to the Interface In JavaScript, sort of

    i have just blogged about inheritance in Javascript, so next target would be programming to the interface.     I had two approaches in mind. The first solution is making an interface as a normal object that contains properties and methods. This object will be static and remain unchanged during the lifetime of the application. The second solution is making the interface using the same way as creating the class in previous post. That means the type of the interface is also a javascript "function". And because i want to use the operator instanceOf in javascript to check whether an object is an instance of a given type so apparently, the first solution will not work. So I have only 1 option and I need to make the interface not to be initialised like a class. Obvisously, you can't create an object with just an interface definition, can you?
    Alright, an interface would have a name and a constract that contains it's members. So I will make an utility which can return a function represents our interface:

function interface(name, contract){
    var _interface = function() {
        if (this._isImplementing !== true){
            throw 'Cannot initialize interface "' + name + '"';
        }    
        
        if (name === '' || typeof name !== 'string'){
            throw 'Must provide interface name';
        }
        
        if (contract == null || typeof contract !== 'object') {
            throw 'Must provide a contract as an object';
        }
        
        this._name = name;            
        this._contract = contract;    
    };        
    return _interface;
}

    I'm using a flag _isImplementing to avoid creating instances using above function. Now I need a method similar to method "Inherit" in previous post but instead of inheriting from a class, it would implement the interface and make the class has all members of the interface's contract:
Object.defineProperty(Object.prototype, "Implement", {
    enumerable: false,
    value: function(anInterface) {        
        if (typeof (anInterface) != 'function') {
            throw 'Can implement and interface only';
        }
        if (typeof (this) != 'function') {
            throw 'Can call method on a function/class only';
        }        
        anInterface.prototype._isImplementing = true;
        this.prototype = new anInterface();
        anInterface.prototype._isImplementing = false;
        
        this.prototype.extend(this.prototype._contract);
        this.prototype._contract = null;
    }
});

    On line 14, I used method extend which just simply copies all members from the contract to "this". So the class will have all members of the contract. You can find the implementation for that method in the file I attach at the end of this post. Finally, with all those stuff ready, i can introduce the demo below:
var IPet = interface('IPet', {
    Name: '', 
    Species: '', 
    GetDescription : function() {
        console.log('function.GetDescription: ' + this.Name + ' is a ' + this.Species);
    }
});
// define a class constructor
var Cat = function(name) {
    this.Name = name;
    this.Species = 'Cat';
}
// make that class implement an interface
Cat.Implement(IPet);

// create another class inherit from the above class
var Tiger = inheritFrom(Cat);


var cat = new Cat('Garfield');
if (cat instanceof IPet) {
    console.log('cat is an instance of IPet');           // Output: cat is an instance of Ipet
}
cat.GetDescription();                                    // Output: function.GetDescription: Garfield is a Cat

var tiger = new Tiger('Tony');
tiger.Species = 'Tiger';
if (tiger instanceof Cat){
    console.log('tiger is an instance of Cat');          // Output: tiger is an instance of Cat
}
if (tiger instanceof IPet){
    console.log('tiger is also an instance of IPet');    // Output: tiger is also an instance of IPet
}
tiger.GetDescription();                                  // Output: function.GetDescription: Tony is a Tiger
 
var tryToInitializeAnInterface = new IPet();             // ERROR: Can not initialize interface "IPet"

Well, the interface should not contain implementation. In fact, I should throw "Not implemented exception" in the contract when i create the interface, and implement that in one of the top class that implements this interface. Honestly, in my real code I would be happy to do like that, but I still keep this mistake for the demo :D Again, comments are welcome!!!
Source code

Cheers

1 comments:

Vo Thanh Toan said...

Hi Thoai, I think this is good idea. However, we need to keep in mind the java script is not real OOP. You just show for us about how to implement interface but another case you should show scenarios and benefits when we using "interface". Some of case you should consider are: inherits many interface, using interface abstract an object. I am looking forward hearing from you.

Post a Comment