miércoles, 20 de febrero de 2008

RFC: Traits para PHP

22-02-2008 Disclaimer: Este es un post computronico. Si esperan algo mas terricola salteen este post.

Leyendo la lista de internals de PHP (version web: http://news.php.net/php.internals) Stefan Marr mando un patch mas que interesante.
Se trata de una implementacion de Traits para PHP.

Pero qué son traits?


Traits son básicamente Behavioral Blocks (en castellano, bloques de comportamiento) que engloban "funcionalidad" (lo pongo entre comillas porque en realidad engloban funciones).

En general estamos acostumbrados (o no, depende del lenguaje de programación...) a usar interfaces y hacer que nuestras jerarquías de clase implementen esas interfaces. Por ejemplo: (en algún lenguaje de programació que se lo banque)


Interface LaInterface
{
   public void metodo1();
   public int metodo2();
   ...
}

interface LaInterface
{
   public void metodoA();
   public int metodoB();
   ...
}


class LaClase implements LaInterface, LaOtraInterface
{
   /* Aca deben estar implementados todos los
     * metodos de LaInterface y LaOtraInterface
     */
}

Pero qué pasa con las jerarquías de clases? A veces tenemos que elegir entre un diseño elegante y bien objetoso, respetando los conceptos que aprendimos en OOP (o_O) como abstracción, encapsulamiento, bajo acoplamiento, polimorfismo, etc. (debo admitir que los tuve que buscar... ya que no me los acordaba...) que respete el problema a modelar o bien, hacer un codigo mas pulenta y que sea mas reusable faltando un poquito nomas, a los principios puristas de la OOP.

Entonces tenemos jerarquías de clases, donde hay clases que extienden a otras, creando familias complejas de padres con hijos herederos de algunos metodos que pueden modificar, o no, visibles dentro de la familia, privados a la clase o publicos al mundo en general...

Pero a veces, tenemos funcionalidades que queremos que muchas familias disfruten, por ejemplo: queremos que todas sepan hacer asado, sean del origen que sean... ya que viven en Argentina.

Entonces supongamos el siguiente ejemplo:



Acá tenemos una jerarquí (no muy util por cierto) que modela familias, de distinto origen. Todas las familias tienen un apellido, y saben tener hijos (espero sepan entender el propósito ilustrativo del ejemplo).
Entonces las familias saben cocinar diferentes tipos de comidas, dependiendo de donde son originarios. En el caso de los tanos: cocinar pasta, risotto y canoli. Los japoneses cocinan sushi y saben tomar sake y los argentinos, sabemos hacer asado, tomar mate e ir a la cancha (yo no, no voy a la cancha :D).

Pero estaría bueno que cualquier familia que viva en argentina sepa cocinar asado y tomar mate (lo mismo vale para cualquier actividad...). Entonces podemos hacer un "paquetito", o Trait (que ni por asomo se parece a un package) con distintas "habilidades". Por ejemplo:

trait CostumbresArgentinas
{
   public void cocinarAsado(){
     // codigo para cocinar asado...
   }
   public void tomarMate(){
     // codigo para tomar mate...
   }
}

Entonces podriamos tener una clase por ejemplo:

class FamiliaItaliana
{
   use CostumbresArgentinas;
   public void cocinarRisotto(){...}
   public void cocinarPasta(){...}
   public void cocinarCanoli(){...}
}

Y tener facilmente una familia italiana que tiene costumbres argentinas (raro no???).

A mi me gusta pensar en una forma de atravesar una jerarquía de clases de manera horizontal, como intento mostrar en este dibujo (click para agrandar):



Traits para PHP



Hay muchos detalles con que lidiar, por ejemplo los conflictos. Qué pasa si 2 traits tienen métodos con igual nombre? quién los rsuelve?

En esta primer implementacion para PHP, los traits son "Aplanados" dentro del c&código de la clase, el propio lenguaje no decide a quién darle prioridad con los conflictos (como se hace en algunos lenguajes) esto se dejo a decision del programador. Uno puede hacer cosas como:

class Hablador {
    use A { !smallTalk }
    use B { !bigTalk }
}

Donde la clase Hablador tiene los metodos del Trait A y del Trait B, pero excluyendo el metodo smallTalk del Trait A y el bigTalk del trait B. También se pueden renombrar métodos al incluir un Trait en una clase.

Una ventaja que tiene esto es que no tiene penalizaci&ocaute;n en runtime, ya que solo es una forma de copy-pastear codigo de una manera formal entre clases.

Les recomiendo leer lo que escribio este hombre Stefan, esta muy bien explicado el caso para PHP.

Bueno, dejo de escribir porque si no se van a aburrir. Espero que se entienda lo suficiente para que si les interesa, lean mas.

/g

Lean aca.
Mas info sobre traits aca.
O pueden leer mas sobre esto en la Tesis de Nathanael Schäarli.

7 comentarios:

Zuker dijo...

tu blog es una nerdeada!!!!

pone fotos y blodeces si queres ser normal!!! jajaja!!! incluso el de resetas de cocina es mucho mejor!!!

un abrazo!!

Anónimo dijo...

sipi, es re aburrido.
y no lo entendi.

Peter dijo...

¿Querés ser normal? Si pusiste un blog para ser normal vas por mal camino... casarte estuvo bien igual.

Y a mi me gustó el post, por ahi tenés que aclarar arriba que es un post computer-scienceoso, asi los que no saben qué es computer-scienceoso no lo leen.

Rafa dijo...

Nerdddddddddddddddddddddd uhhhhhh pero algo entendi de eso, espero no ser tambien un nerd :(

Gutes dijo...

Bueno, ademas de los comentarios barderos gracias piter por recomendar algo serio.

Agregué una especie de disclaimer que voy a usar de acá en adelante.

aurelianito dijo...

Groso lo de los traits para PHP, en Ruby hay algo parecido, pero el manejo sobre los conflictos las cosas que incluís se manejan distinto. Si un método ya está en la clase no se agrega (y esa decisión es cambiable).

Y no te dejes patotear por los no-nerds. Ser normal no tiene onda. Si te gustan estas cosas, escribí de estas cosas.

Jtux dijo...

Traits para PHP vendra nativo en PHP 6, asi que el POST vale la pena