Friday 14 September 2012

Prototype-based inheritance in Titanium mobile framework



The problem


Since I was working on large in-house mobile application, I decided to wrap Titanium API (in my opinion, it's good practice to wrap third-party code in general). I've built wrapper for every Titanium component because I thought it would be easier to fix something in the wrapper than in whole application when particular Titanium component brakes, or changes its functionality. Here, I'll concentrate on UI wrappers.

In last wrappers that I used, I used configuration objects in OOP (described in one of previous posts). That approach was not very satisfying to me because of few reasons. That approach was not prototype based, rather I would copy objects, which led to slower performance and bugs (shallow VS deep object copy). What I also mind about it, is that approach just does not fit well into JS language and larger projects.


I wanted to develop something faster and intuitive that would feel natural to JS and Titanium in particular. Also, Titanium SDK moved towards CommonJS approach, so I wanted to develop something that would fit into CommonJS.

But before that I needed to learn native OOP in JS (and not how to emulate classes or something like that), which is prerequest for anyone who wants to understand this article. I will explain it briefly here (I'll skip explanation of "this" value - everything you need to know about "this" value can be found here: http://dmitrysoshnikov.com/ecmascript/chapter-3-this/ ).


OOP in JavaScript


OOP in JavaScript is very hard to truly understand for someone who comes from class-based programming languages, although it's very simple.


JavaScript is object-based OO language. So, it doesn't have classes, it has prototype objects.


New objects are created with functions, there are no constructors like in class-based languages where constructors are part of the class. Functions can be called in few ways - as regular function or as function constructor (that is, function that constructs new objects). Note that same function can be called as regular function and as function constructor, those two functionalities are not separated, it's just a matter of how function is called:

function func() {

}

func(); // regular call
var obj = new func(); // call as constructor


So, what happens if this function is called as constructor? This seems pretty obvious in this particular case - new empty object "{}" will be created, but there are few details that are missing in that picture.

Every function has "prototype" property that contains prototype object reference and when that function is called as function constructor, newly created object will have its internal "__proto__" property set to "func.prototype". That's a link between newly created object and its prototype object. That property cannot be read directly, instead "Object.getPrototypeOf" must be used:


Object.getPrototypeOf(obj) === func.prototype; // true


"this" value inside function constructor is set to newly created object and it is returned from function unless something else is returned.


function func() {
   this; // {} - newly created object
   return { dontDoThis: true };
}


var o = new func(); // { dontDoThis: true }


By default, every function has "prototype" property set to "Object.prototype" and therefore, newly created objects will have their "__proto__" property set to point to "Object.prototype".



Let's add some properties and methods to prototype object:

function Guide() {

}

Guide.prototype = {
    theAnswer: 42,
    theQuestion: 'What is the Answer to the Ultimate Question of Life, the Universe and Everything?',
    askQuestion: function() {
         return this.theQuestion;
    },
    answerToTheQuestion: function() {
         return this.theAnswer;
    }
};

var hitchhiker = new Guide(); // {}.__proto__ -> Guide.prototype
var anotherHitchhiker = new Guide(); // {}.__proto__ -> Guide.prototype 

hitchhiker === anotherHitchhiker; // false
// but
Object.getPrototypeOf(hitchhiker) === Object.getPrototypeOf(anotherHitchhiker); //true

So, this newly created objects are different objects, but they have same prototype object. In some sense, prototype object is what class is in class-based languages.


If properties of newly created objects are added or modified, that change is going to be reflected only on them. We can say that they have they own property.

For example, "hitchhiker" has no its own properties (as I mentioned, it's empty object), although "theAnswer" property can be read:

hitchhiker.theAnswer; // 42

Basically, when property is read, firstly object is check for existence of that property, if object has no its own property, its "__proto__" object is checked by same algorithm until reached object has no "__proto__" set ("Object.prototype.__proto__" is "null"). We can say that object and its prototype objects form prototype chain.

So ...

hitchhiker.hasOwnProperty('theAnswer'); // false

but ...

hitchhiker.theAnswer = 11;
hitchhiker.hasOwnProperty('theAnswer'); // true

// hitchhiker -> { theAnswer: 11 }.__proto__ -> Guide.prototype -> { theAnswer: 42, ... }

Note that prototype object is not modified, the algorithm now finds property right away and prototype chain is not searched for that property.


You can modify prototype object from "hitchhiker" object, but that is not smart thing to do:




If prototype object is modified, all objects that have that object as prototype object will also be modified.



So ... inheritance in JavaScript is implemented through prototype chains and definition of object's own properties.If you want to extend some object, use it as prototype object and add or override its properties.

function ExtendGuide() {

}

ExtendGuide.prototype = new Guide(); // new Guide = {}.__proto__ -> Guide.prototype

ExtendGuide.prototype.theAnswer = 11;



var newHitchhiker = new ExtendGuide(); // {}.__proto__ -> ExtendGuide.prototype

// comment shows property resolution
newHitchhiker.theAnswer // newHitchhiker --> newHitchhiker.__proto__  == ExtendGuide.prototype ~ [theAnswer]


>newHitchhiker.answerToTheQuestion; // newHitchhiker --> newHitchhiker.__proto__ == ExtendGuide.prototype --> ExtendGuide.prototype.__proto__ == Guide.prototype --> Guide.prototype ~ [answerToTheQuestion]


Usually, developers make mistakes in approach when they want to override some method. When they come to the realm of JavaScript, they still have class-based way of thinking and they want to override some method by modifying its functionality and calling parent method (super-method).



The super idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns.




That "super" method pattern is really unnecessary. Unfortunately, some developers had gone so far that they use "this.super()" and that is:






If you see something like that, run away, that's voodoo magic. :)



The rule of not calling "super" method is simple - if you think that you need to call "super" method, refactor that "super" method and think again, otherwise proceed.


In most cases, that can be achieved by splitting that "super" method to few smaller methods and simply overriding particular method.

For example:


function ExtendExtendedGuide() {

}

ExtendExtendedGuide.prototype = new ExtendGuide();

ExtendExtendedGuide.prototype.askedTimes = 0;

ExtendExtendedGuide.prototype.super = function() {
    this.askQuestion();
    this.askedTimes++;
};

Now, if I want to increase "askedTimes" by 2 and not by 1, in overridden method I could first call "super" and then again increase "askedTimes".

function CallSuperGuide() {

}

CallSuperGuide.prototype = new ExtendExtendedGuide();

CallSuperGuide.prototype.super = function() {
    ExtendExtendedGuide.prototype.super.call(this);
    this.askedTimes++;
};

But, I could also refactor that part of "super" function:

ExtendExtendedGuide.prototype.super = function() {
    this.askQuestion();
    this.increaseAskedTimes();
};


ExtendExtendedGuide.prototype.increaseAskedTimes = function() {
    this.askedTimes++;
};

and then I would not need to call "super" method:


function NoSuperGuide() {

}

NoSuperGuide.prototype = new ExtendExtendedGuide();  


NoSuperGuide.prototype.increaseAskedTimes = function() {
    this.askedTimes += 2;
};







Hopefully, now you understand OOP in JavaScript. Let's go to Titanium specifics.



Approaches in writing Titanium wrappers


There are more approaches that came from Titanium community, but they all have one thing in common. Native JS object acts as proxy object for Titanium UI object. Something like this:


function View(props) {

  var viewWrapper = {};
  viewWrapper.Element = Ti.UI.createView(props);

  //do something with wrapper like add a method
  viewWrapper.doSomething = function() {};
  return viewWrapper;
}

var view = View({ height: 42 });


More efficient way of doing this would be this:


function View(props) {
  this.Element = Ti.UI.createView(props);
}

View.prototype.doSomething = function() {};

var view = new View({ height: 42 });


This approach has a problem in object-oriented programming because this newly created object cannot be used as function's prototype object.


function ExtendedView() {}

ExtendedView.prototype = new View(); // only one TiUIView is created!

var view1 = new ExtendedView(); // Element property points to same TiUIView through prototype object
var view2 = new ExtendedView(); // but two objects are created


One way to solve that issue is calling super-constructor ("View.call(this)") in every function constructor that extends "View". That solution is not the best one because super-constructor calls are growing with every new extension.


ExtendedViewN -> ... -> ExtendedView2 -> ExtendedView1 -> View (weee, we're done)


The other way that I found on the internet that solved this problem is calling "init" method in function constructor:


function View() {
  this.init(); 
}
View.prototype.init = function(){
  this.Element = Ti.UI.createView();
};

function ExtendedView() {
 this.init(); 
}


ExtendedView.prototype = new View();
That solution is fine and better than previous one, but "init" function has to be called in function constructor if ExtendedView is going to be extended.



I wanted to make more Zen-ish solution. :)



Theoretical approach in ZenTi




The problem in approaches described above is that Titanium UI objects are created in function constructors. Function constructors are outside entities and they do not belong to prototype objects (not like constructor in classes which belong to particular class) and many developers put logic there, which is semantically wrong. Function constructors do construct new objects, but they need to be used only for setting some object-specific data.


//for example

//wrong
function ConstructSomething(something) {
   if (something == 'Something') {
      this.doSomething();
  }
}

//better
function ConstructSomethingElse(something) {
   this.init(something);
}
 ConstructSomethingElse.prototype.init = function(something) {
   if (something == 'Something') {
      this.doSomething();

  }



So ... to create Titanium prototype-based wrapper, TiUI component needs to be constructed in the object that models wrapper and not in function constructor. It took me a while to figure out how to do that, but the basic idea is quite simple. The trick here is that TiUI component does not need to be created right away, it can be created when it is needed. There are a lot of moments when TiUI component is needed, but all moments have one thing in common - they all want to get TiUI component. So, instead of using (static) property, I'm using (dynamic - so to say) method that tries to read property that holds a reference to TiUI component and if that property does not contain TiUI component, it creates it first.


Basic implementation can be seen in this Gist: https://gist.github.com/3314660


Described approach in that gist has a problem: https://jira.appcelerator.org/browse/TIMOB-10829

But that can be solved by using "Object.defineProperty" and define properties with "enumerable" set to "false".




"__getTiElement" is main function that shows lazy creation of TiUIView (I use similar pattern often). If something needs to use TiUIView, it won't use "__TiElement" property that actually holds TiUIView directly. Rather, "__getTiElement" will be used. For example, "addEventListener" implementation would look like this:

View.prototype.addEventListener = function(type, listener) {
  this.__getTiElement().addEventListener(type, listener);
}



So ... basic workflow of some component would be:

var test = new View(); // creates new empty object - {} with "View.prototype" as prototype
test.addEventListener('click', function() {}); //{} -> "View.prototype.addEventListener" -> "View.prototype.__getTiElement" -> "create test.__TiElement (via this)" -> "test.__TiElement.addEventListener (via this)"


As you can see, TiUIView is not created right away, it is created when needed.



Conclusion



This was theoretical explanation of basic concept of prototype-based OOP in ZenTi. It can be used to implement prototype-based OO inheritance in Titanium in general.

ZenTi is relatively large, I wanted to create library that would help me easily develop highly reusable CommonJs modules and I wanted to have some functionality that Titanium lacks. Also, I wanted to avoid direct usage of Titanium API as much as possible and I wanted to have something reliable.
There are some things that needs to be fixed and refactored in it and it can be faster - I created some overhead in favor of reliability that can be avoided, because of nature of application that I was developing, speed wasn't the most important thing (but it was important).


All that and ZenTi architecture in detail will be topic of my next posts. Hopefully I'll release it in near future (I terribly lack free time). But that does not prevent you from using this approach in your applications.

1 comment:

  1. Nice to see, that was a interesting article. The trick here is that TiUI component does not need to be created right away, it can be created when it is needed.Thanks
    iPod touches

    ReplyDelete