sábado, 11 de diciembre de 2010

Lecciones: Herencia de Clases en JavaScript para webOS (parte 3 de 3)

Traducir / Translate

Continuamos esta serie de artículos sobre la herencia de clases en JavaScript para webOS.

En la parte 1 vimos la creación de clases y métodos de distintas formas. La más práctica es usando
los mecanismos del framework Prototype. En la parte 2 nos concentramos cómo llamar a métodos de la clase padre, visibilidad de parámetros y algunas rutinas de uso común.

En esta última parte vamos a ver algunas rutinas curiosas adicionales, y hablaremos de cómo expandir jerarquías de clases y algunas advertencias a tener en cuenta.

Veamos rápidamente un ejemplo:



var TRUE = true;
var FALSE = false;


/**
* @return boolean TRUE si child es instancia (o descendente) de father. FALSE en cualquier otro caso.
*/
var isInstanceOf = function(child,father) {
  if (child == father) return TRUE; //es instancia de si mismo.
  if (undefined == child.superclass) return FALSE; //no tiene ningún ascendente.
  return isInstanceOf(child.superclass,father); //revisar el proximo ascendente, recursivamente.
}


var Abuelo = Class.create();
var Padre = Class.create(Abuelo);
var Hijo = Class.create(Padre);
var Hermano_del_Padre = Class.create(Abuelo);
var Biznieto = Class.create(Hijo);
var write = function(text) { document.write(text + "<br/>\n"); }


write("");
write("test 1: " + isInstanceOf(Padre,Abuelo)); //true
write("test 2: " + isInstanceOf(Hijo,Padre)); //true
write("test 3: " + isInstanceOf(Hijo,Abuelo)); //true
write("test 4: " + isInstanceOf(Hermano_del_Padre,Abuelo)); //true
write("test 5: " + isInstanceOf(Hijo,Hermano_del_Padre)); //false
write("test 6: " + isInstanceOf(Biznieto,Abuelo)); //true

Ahora las explicaciones: Tenemos una familia completa de clases, desde el Abuelo hasta el Biznieto. Con la rutinas isInstanceOf mostramos el uso de una propiedad de todas las clases, propia del framework Prototype: superclass. Ésta guarda una referencia a la clase padre, proveniente del comando Class.create(<clase padre>,...)

Ahora vamos a hablar de extender jerarquías. 

Esta lista no es exhaustiva, pero los casos más comunes son los siguientes:

1.- Padre >> Hijo ---- Padre >> Intermedio >> Hijo. 
Este es el caso más común. Resulta que se quiere crear otra clase Hijo (Hijo2) diferente de la anterior, pero no mucho. Hijo2 compartirá parte del código de Hijo, pero no todo. La mejor forma de hacer este cambio SIN AFECTAR al resto de la jerarquia ni a las clases que dependen de Padre y de Hijo, se hacen dos cosas:
paso 1.- Se crea la clase Intermedia como descendiente de Padre y la clase Hijo se hace descendiente de Intemedia. Esto garantiza que no se afecta al resto de las clases.
paso 2.- Se pasa a Intermedia el código de Hijo que se quiera reutilizar en Hijo2, que desciende de Intermedia. Lo que se logra es evitar la duplicidad del código que se quiere utilizar en la nueva clase.

2.-Padre >> Hijo --- Abuelo >> Padre >> Hijo.
Este caso también es muy común. Sirve muy bien para agregar código a las clases Padre e Hijo, dejándolas casi intactas (es necesario modificar la clase Padre para que llame correctamente a las clases que provienen de Abuelo. La clase Hija sí que se queda intacta).

3.- Padre1 >> Hijo1 y Padre2 >> Hijo2 ---- Hijo1 e Hijo 2 >> Nieto ??!!!
Esto es la multiherencia. Se quiere que la clase Nieto tenga funcionalidad tanto de Hijo1 como de Hijo2. JavaScript no admite multiherencia, no por mecanismos regulares. Hay alternativas, pero la razón de no exista directamente esa posibilidad es que no es muy recomendable (claro, hay casos de casos!). El modelo de contenedor es la respuesta más sencilla: Nieto no desciende de Hijo1 (y hasta ni de Hijo2), sino que dentro tiene un objeto que desciende de Hijo1 (y posiblemente otro de Hijo2). Para acceder a los métodos y propiedades de Hijo1 desde el exterior de Nieto, habrá que proveer de alguna función para hacerlo, si es que es necesario. Generalmente no hace falta, pero finalmente ésta es una decisión del diseño, materia en la cual no hay reglas fijas e inamovibles.

Ejemplo 1:
var Nieto = Class.create(Hijo1);
Nieto.addMethods({ initialize : function() { this.de_hijo2 = new Hijo2(); }});
//Nieto tiene la funcionalidad de Hijo1 y también contiene un objeto que desciende de Hijo2.

Ejemplo 2:
var Nieto = Clase.create();
Nieto.addMethods({
  initialize : function() {
    this.de_hijo1 = new Hijo1();
    this.de_hijo2 = new Hijo2();
  }
}); //Nieto tiene la funcionalidad deseada en dos objetos que descienden de Hijo1 e Hijo2.

Ahora las advertencias y las recomendaciones.

.- Las grandes jerarquías no son ningún problema (ni se rendimiento ni de mantenibilidad) siempre y cuando estén perfectamente documentadas y se sepa exactamente las diferencias entre cada una de las clases. Todos los sistemas de escritorios conocidos (KDE, GNOME, Windows, etc.) tienen inmensos sistemas de jerarquías para poder mostrar ventanas, iconos, botones, etc. Pero todo está perfectamente ordenado y documentado.

.- Para aumentar la jerarquía con clases que apenas se distinguen en uno o dos métodos y/o propiedades se deben tener muy fundamentadas razones para hacerlo, porque generalmente son producto de un diseño pobre o no muy bien pensado. Tampoco se trata de crear tantas clases como sea posible, sino lo mínimo imprescindible que permita alcanzar los objetivos y producir código estable y reutilizable.

.- La más importante de las advertencias: la jerarquía de clases debe tomarse en cuenta en cada modificación que afecte a sus descendientes. Hay que recordar que los daños (y los beneficios) se extienden como la pólvora a través de todas las clases hijas que dependen de las clases padres modificadas, y eso exige tener mucho cuidado y asumir el compromiso de hacer pruebas suficientes para demostrar que no se crean errores en cascada en el proceso de modificación.


Espero que estas lecciones os sean de provecho en su camino a través de JavaScript.

Saludos,
Herman.

0 comentarios:

Publicar un comentario