Sunday, February 16, 2014

Javascript inheritance:A cautionary tale

I am currently working on a project that uses the Google Maps V3 API as a frontend.I had to create Polygons and display it on the map,sounds simple enough right.I took a look at the samples and did this:
 var polyPoints=[];  
      polyPoints.splice(0,0,new google.maps.LatLng(-34.9290,138.6010),new google.maps.LatLng(-37.8136,144.9631),new google.maps.LatLng(-27.4679,153.0278));  
 var polygon=new google.maps.Polygon({  
   paths: polyPoints,  
   strokeColor: '#FF0000',  
   strokeOpacity: 0.8,  
   strokeWeight: 2,  
   fillColor: '#FF0000',  
   fillOpacity: 0.35  
  });  

I was trying to be clever,see how I take something very simple and twist it around.I had just discovered that you could add items to an Array using slice and I wanted to use it everywhere.In a moment we will see how this tendancy of mine leads me into trouble.

I had just learnt to use functions as constructors and here,standing right before me was a case where this idea could be applied to:

 var configurePolygonOptions=function configurePolygonOptions(strokeColor,strokeWeight,strokeOpacity,fillColor,fillOpacity)  
 {  
      this.strokeColor=strokeColor;  
      this.strokeWeight=strokeWeight;  
      this.strokeOpacity=strokeOpacity;  
      this.fillColor=fillColor;  
      this.fillOpacity=fillOpacity;  
 };  

An object is then instantiated using the new keyword which calls a function using the constructor form and sets this to the object created and  the object's constructor property to be the function itself.However this object can only be used to initialize the Polygon's properties and goes no further than that(as I later found out).By the time this key piece of information reached the evil genius(a movie cliche bound to fail) had decided to go overboard and do this:


 configurePolygonOptions.prototype.setStrokeColor=function setStrokeColor(color)  
 {  
      this.strokeColor=color;  
 };  
 configurePolygonOptions.prototype.setStrokeWeight=function setStrokeWeight(weight)  
 {  
      this.strokeWeight=weight;  
 };  
 configurePolygonOptions.prototype.setStrokeOpacity=function setStrokeOpacity(weight)  
 {  
      this.strokeWeight=strokeWeight;  
 };  
 configurePolygonOptions.prototype.setFillColor=function setFillColor(color)  
 {  
      this.fillColor=color;  
 };  
 configurePolygonOptions.prototype.setFillOpacity=function setFillOpacity(opacity)  
 {  
      this.fillOpacity=opacity;  
 };  
 configurePolygonOptions.prototype.setProperty=function setProperty(prop,value)  
 {  
      this[prop]=value;  
 };  
 configurePolygonOptions.prototype.setPaths=function setPathsForPolygon(paths)   
  {    
    this.paths=paths;   
    console.log('The points have been added to polygon options');   
  };   
  configurePolygonOptions.prototype.setEditable=function(edit)   
  {   
    this.editable=edit;   
  };   
  configurePolygonOptions.prototype.setDraggable=function(drag)   
  {   
    this.draggable=drag;   
  };   

So,all this code is useless,but then I look at the API for drawing a circle and a rectangle and say to myself "How about trying out that inheritance concept you just learnt about?"


 var createRectangle=function createRectangle(strokeColor,strokeWeight,strokeOpacity,fillColor,fillOpacity)  
 {  
           configurePolygonOptions.apply(this,[strokeColor,strokeWeight,strokeOpacity,fillColor,fillOpacity]);  
 };  
 createRectangle.prototype=Object.create(configurePolygonOptions.prototype);  
 createRectangle.prototype.constructor=createRectangle;  
 createRectangle.prototype.setBounds=function(bounds)  
 {  
      console.log('setting bounds');  
      this.bounds=bounds;  
 };  
      var configureCircle=function configureCircle(strokeColor,strokeWeight,strokeOpacity,fillColor,fillOpacity)  
     {  
           configurePolygonOptions.apply(this,[strokeColor,strokeWeight,strokeOpacity,fillColor,fillOpacity]);  
           //configurePolygonOptions.call(this);  
};  
configureCircle.prototype=Object.create(configurePolygonOptions.prototype);  
configureCircle.prototype.constructor=configureCircle;  
configureCircle.prototype.setCenter=function(center)  
{  
     this.center=center;  
};  
configureCircle.prototype.setRadius=function(radius)  
{  
     this.radius=radius;  
};  

There was so much extra code but with no apparent problems,so I had to go create one,I noticed a map parameter within the object used  for Circle and Rectangle,I should have passed it in as an extra parameter and let it slide but I thought what if I could add a setMap method to configurePolygonOptions.prototype  and I would automatically inherit it(just like JAVA)

And here it is,after adding this code in,I found that Polygons would not be rendered on the map:



 configurePolygonOptions.prototype.setMap=function(map)  
 {  
      this.map=map;  
 };  
 var createPolygon=function createPolygon(options)  
 {  
      var pgon= new google.maps.Polygon(options);  
      console.log('created a polygon based on options provided');  
      return pgon;  
 };  
 var addPolygonToMap=function addPolygonToMap(poly)  
 {  
      poly.setMap(map);  
      console.log('added polygon to map');  
 };  

After trying a few different things like putting console.log methods evrywhere regardless of their relative,yelling at the computer,I posted a question on stackoverflow.When you create an instance of the Google Maps v3 API's Polygon object using an object you pass it,instead of protecting it's private members it instead allows overriding any properties the object has.(Yeah,blame the API and not yourself for not using the API the way you were meant to) and I had overridden the setMap method of Polygon which Circle and Rectangle did not have.

Should Google's API have protected the properties inside their object and only offer them for modification using methods like say,they would in Java?I do not know the answer to that,coming from a OO background,I would like that but...is it for everyone?

Lessons learned:

1.Keep your code simple,dont show off.
2.Use an API how it is meant to be.
3.Inheritance is evil.Use with care.(Are mixins evil too?)

Please comment and let me know what you think.

No comments:

Post a Comment