Archive for August, 2006

Programacion Orientada a Objetos

Thursday, August 24th, 2006

Hoy se me ocurrió buscar en Google “programación orientada a objetos” y honestamente estoy muy decepcionado con los resultados en primera página. El único resultado que mas o menos dice algo interesante es el de la Wikipedia, pero al tratarse de una enciclopedia están obligados a decir todo lo que está relacionado con el tema, y eso puede llegar a oscurecer la verdadera idea del tema, y en especial puede asustar a los que no saben del tema.

Entonces, ¿Qué es realmente la programación orientada a objetos?

Ante todo, y para simplificar un poco la explicación, ayudaría bastante ya saber algo de programación (if, for, while, procedimientos, funciones, etc.); en especial, haber escrito código anteriormente, sin importar el lenguaje (la mayoría de los lenguajes son todos iguales).

Ya antes escribí algo sobre el tema, pero decía las cosas desde otro punto de vista. Este artículo está pensado para principiantes que ya saben programar y quieren aprender que es realmente la POO (“programación orientada a objetos”).

Breve historia de la programación

Contrariamente a la creencia popular de que Microsoft inventó la computadora, que Java es la salvación a todos nuestros problemas terrenales y .Net de Microsoft es el camino hacia la iluminación espiritual, la programación en general (y orientada a objetos en particular) ya existía muchas décadas atrás; incluso antes de que a James Gosling se le ocurra hacer un lenguaje de programación para hacer que su licuadora haga los daikiris a punto.

La programación orientada a objetos nació en los años 60 en Noruega, de la mano del lenguaje conocido como Simula. Si, es un lenguaje muy “viejo”, pero aun así ya tenia varios elementos que VB.Net incluye como una “novedad”. Tenía clases, herencia, encapsulamiento y (agarrate) polimorfismo, también conocido como funciones virtuales; a través del “binding dinamico”.

Luego apareció Smalltalk en la decada de los 80, después apareció C++ a fines de los 80 y, a mediados de los 90, Java. Mas tarde Microsoft se dio cuenta de que Java se llevaba toda la plata y, como también querían ganar algún dinero extra, en año 2002 nació .Net.

Por último, Linus Torvalds opinó que Visual Basic fue una gran contribución a la industria de la programación, mas que la POO, y se pusieron todos como locos.

Si no me creés, mira esto: http://www.kriptopolis.org/node/2713

¿Que tiene de diferente la POO?

Pregunta tramposa: ¿Qué tiene de diferente en comparación a qué?

Con respecto a un gato, los lenguajes de programación no hacen “miau” (al menos hasta donde tengo entendido :P)

Con respecto a Visual Basic o Pascal, éstos dos son lenguajes Procedurales, o sea, uno lo que hace es crear procedimientos y funciones para modularizar el problema, descomponerlo en partes y facilitar la resolución del problema, y todo eso.

PHP 5 es multiparadigmatico (muchos paradigmas de programacion): permite programar usando funciones normales o hacer uso de las bondades de la POO.

Por ejemplo, si lo que uno quiere es hacer un programa en PHP (usando programación procedural clásica) que dado un nombre de una fruta te diga el color de su cáscara (la parte de afuera de la fruta), sería algo así:

PHP:
  1. function dar_color_cascara_fruta($nombre_fruta)
  2. {
  3. switch ($nombre_fruta)
  4. {
  5. "banana": return "amarillo";
  6. "pera": return "amarillo";
  7. "kiwi": return "marron";
  8. "maznana": return "rojo o verde";
  9. "pomelo": return "amarillo o rosáceo";
  10. default: return "fruta no reconocida";
  11. }
  12. }
  13. echo dar_color_cascara_fruta("banana"); // "amarillo"
  14. echo dar_color_cascara_fruta("manzana"); // Fruta no reconocida!!

El programa anterior tiene varios problemas. Ante todo, “manzana” está mal escrito en el “switch”. En realidad es un error muy simple, hay que corregir el orden de dos letras, ¿no? Si.

Muy bien. Entonces si el usuario me ingresa el nombre de una fruta yo puedo decirle fácilmente su color.

Ahora, ¿que pasa si, además de querer mostrar el color de una fruta, quiero mostrar su “familia”? Es muy simple, hay que hacer otra función que haga algo parecido:

PHP:
  1. function dar_familia_fruta($nombre_fruta)
  2. {
  3. switch ($nombre_fruta)
  4. {
  5. "banana": return "Musaceae";
  6. "pera": return "Rosaceae";
  7. "kiwi": return "Actinidiaceae";
  8. "manzana": return "Rosaceae";
  9. "pomelo": return "Rutaceae";
  10. default: return "fruta no reconocida";
  11. }
  12. }
  13.  
  14. echo dar_familia_fruta("melon"); // Fruta no reconocida!!

Ahora, ¿que pasa si quiero hacer mas cosas con las frutas? Tengo que hacer más funciones.

Hermoso. Pero hay otro problema: solo tengo 5 frutas. ¿Que pasa si quiero meter más frutas, digamos, otras 211 frutas?

Acá empiezan los problemas: tengo editar cada una de las funciones y, en cada una de ellas meter los elementos en el “switch” correspondiente. Y Dios no quiera que nos equivoquemos al escribir una fruta, o peor aún, ¡nos olvidemos de alguna!

¡Momento! Dirán algunos. Capaz estoy haciendo algo de trampa. Elegí PHP porque hoy en día la mayoría de los que saben programar, entienden PHP. Y en PHP se puede usar una base de datos, y todo eso.

Pero que pasa si, en vez de pedir información “sencilla” de las frutas, en vez de pedirle a una fruta algo que se puede guardar en una base de datos, le pido hacer algo complejo con las frutas? Por ejemplo “pelar la fruta”, o “comer la fruta”, o “cocinar la fruta”, o “cortar la fruta”. A cada fruta hay que cortarla de una forma particular, e incluso algunas personas la cortan de una manera, y otras de otra manera. Yo por ejemplo nunca le saco la cáscara a la manzana.

O cualquier otra funcionalidad que a tu imaginación se le ocurra hacer con una fruta.

Programación Orientada a Objetos

La POO plantea las cosas diferente. En vez de hacer un procedimiento con todas las frutas y tener todo “centralizado” en una colosal función que tenga la lista de todas las frutas, y de acuerdo a la fruta haga lo que tiene que hacer, en vez de eso, un programa hecho y diseñado en objetos deja que cada fruta decida que hacer.

El programa, a cada fruta, le delega la responsabilidad de comportarse como una fruta de su clase. Una verdadera, completa e íntegra fruta.

O sea, de la misma forma que yo se que tengo dos ojos, una nariz, una boca, nací en la ciudad de La Plata, y me llamo Alejandro, una manzana sabe que ella misma es una manzana roja de la familia de las “Rosaceae” y sabe como hay que hacer para que alguien se la coma y como hay que hacer para que alguien la corte en pedacitos.

O al menos eso debería saber la fruta en cuestión.

¿Eh? ¿Y como se hace eso?

Es muy fácil, hay que hacer un objeto por cada fruta. Más precisamente, hay que hacer una clase por cada fruta, y luego crear el objeto desde su clase:

Atención: Aquellos fanáticos de PHP les comento que estoy utilizando la versión 5. PHP4 maneja los objetos de diferente forma, PHP5 lo hace más acorde a lo que la “POO” representa.

PHP:
  1. class Kiwi
  2. {
  3. function dar_color_cascara()
  4. {
  5. // Yo se que soy un kiwi y soy marron
  6. return "marron";
  7. }
  8.  
  9. function dar_familia()
  10. {
  11. // ...
  12. return "Actinidiaceae";
  13. }
  14. }
  15.  
  16. class Banana
  17. {
  18. function dar_color_cascara()
  19. {
  20. // Yo se que soy una Banana y se que soy amarilla
  21. return "amarillo";
  22. }
  23.  
  24. function dar_familia()
  25. {
  26. return "Musaceae";
  27. }
  28. }
  29.  
  30. // Creo un nuevo objeto Banana
  31. $f = new Banana();
  32.  
  33. // Le pregunto al objeto lo que quiero saber
  34. echo $f->dar_color_cascara(); // Muestra el color de la cascara
  35. echo $f->dar_familia(); // Muestra el nombre de la familia

No es taaaaaaan complicado, ¿no? En la variable “$f” se guarda una referencia al objeto “Banana”, por lo que si trabajo con $f voy a estar trabajando con una Banana.

Se dice que lo que hago es enviarle un “mensaje” a la clase banana preguntándole el color de la cáscara, y la banana responde el mensaje diciendo el color. En realidad no es más que llamar a lo mismo con otro nombre, y en este ejemplo no hay ningún “server de mail para el envio de mensajes entre objetos”. No, no hay nada de eso.

Decir “mensaje” es sinónimo de decir “llamo al método”, donde el “método” es el nombre de la “función” de ese objeto.

¿Se acuerdan de la manzana en el primer ejemplo? ¿Que pasa si ahora hago esto?

PHP:
  1. $f = new Maznana();

Antes, el error me aparecia cuando ejecutaba el programa, y preguntaba por la “Maznana”. Ahora, en cambio, aparece cuando el compilador intenta compilar el programa, ayudándome a encontrar errores. (PHP es un caso particular, pero supongamos que lo compila ^^, o, sino, el error apareceria cuando se hace el “new Maznana()”).

Que lindo, ¿no?

¿Que pasa si quiero 3 Bananas?:

PHP:
  1. $f1 = new Banana();
  2. $f2 = new Banana();
  3. $f3 = new Banana();

Acá tengo 3 bananas. Las tres variables guardan, cada una, una referencia diferente a cada una de las instancias de la clase Banana.

Supongamos ahora que las bananas tienen dueño. Pedrito y Juancito, cada uno son dueños de una banana. ¿Como hacemos eso? Hay que modificar la clase Banana y agregarla una variable de instancia, de la siguiente forma:

PHP:
  1. class Banana
  2. {
  3. private $m_duenio = ""; // (1)
  4.  
  5. function __construct($duenio) // (2)
  6. {
  7. $this->m_duenio = $duenio; // (2b)
  8. }
  9.  
  10. function dar_nombre_duenio() // (3)
  11. {
  12. return $this->m_duenio;
  13. }
  14.  
  15. function dar_color_cascara()
  16. {
  17. // Yo se que soy una Banana y se que soy amarilla
  18. return "amarillo";
  19. }
  20.  
  21. function dar_familia()
  22. {
  23. return "Musaceae";
  24. }
  25. }

Nuestra clase Banana está tomando mas forma (forma a Banana ^^)

Bien, ahí puse varias cosas.

Primero: Donde dice “(1)” dentro de la clase Banana puse una variable de instancia llamada “$m_duenio”, la cual sirve para guardar el nombre del dueño de la banana en cuestión.

Las variables de instancia se usan para guardar propiedades en cada objeto. Cada instancia de la case Banana tiene su propia y privada propiedad que le permite a la banana “conocer” a su dueño. Es así que cada banana puede tener su propio dueño.

Al indicar la variable como “private” estamos diciendo que desde el único lugar que se puede modificar directamente el valor de la variable es desde la propia clase. Para modificar el valor de la variable de instancia desde otro lado hay que usar una función de instancia que modifique el valor, indicando por parámetro el nuevo valor.

El nombre de la variable es bastante extraño a primera vista, si, pero es lo que se conoce como “convención” de nombres. Hay quienes dicen que todas las variables de instancia se recomienda que empiecen con “m_” (“miembro”) de forma que a simple vista sea posible darse cuenta que se trata, efectivamente, de una variable de instancia. En realidad en PHP no hace falta, porque como se muestra en “(2b)” todas las variables de instancia hay que accederlas con $this-> desde dentro del objeto.

Segundo: Donde dice “(2)” puse una nueva funcion llamada “__construct” la cual se ejecuta cada vez que se crea una nueva instancia de una Banana (dado que esa función está dentro de la clase Banana y el manual de PHP dice que eso es así porque es así). Además, tiene un parámetro para indicarle el nombre del dueño de la banana.

Tercero: Como dije antes, donde dice “(2b)” le estoy asignando a la variable de instancia el valor indicado por parámetro. Para ello utilizo la mágica expresión “$this-> m_duenio” donde $this es una variable “especial” de PHP que representa la instancia del objeto mismo. Es como decir “yo”.

$this es posible usarlo solo dentro de las funciones de una clase, y referencia a esa instancia en particular.

Cuarto: Donde dice “(3)” puse un nuevo “método” (otro nombre para decir “función”) dentro de clase Banana que sirve para ver el dueño de una banana.

¿Como uso todo esto y para que me sirve?

Antes les dije que Pedrito y Juancito tenían bananas, pero lo que ellos querían era que las bananas conozcan a su dueño, así cuando uno le pregunta a la banana “¿quien es tu dueño?” te responde apropiadamente. Y para no olvidarnos de que todas las bananas tienen dueño, el nombre del dueño de cada banana hay que indicarlo al momento de crear cada banana. Por ejemplo:

PHP:
  1. $f1 = new Banana("Pedrito");
  2. $f2 = new Banana("Pedrito");
  3. $f3 = new Banana("Juancito");

Acá tenemos 3 bananas, otra vez. Pero ahora, las bananas tienen dueño. Las dos primeras son de Pedrito, y la tercera es de Juancito. Entonces:

PHP:
  1. echo $f1->dar_nombre_duenio(); // Imprime “Pedrito”
  2. echo $f2->dar_nombre_duenio(); // Imprime “Pedrito”
  3. echo $f3->dar_nombre_duenio(); // Imprime “Juancito”

Si yo por ejemplo me encuentro tirada una banana por la calle, y quiero saber quien es el dueño de esa banana, solo tengo que preguntarle. Todas las bananas conocen a su dueño y por lo tanto me lo va a saber decir.

PHP:
  1. // $f tiene una banana cualquiera, desconocida
  2.  
  3. echo $f-> dar_nombre_duenio();

Problema resuelto. Muy bien. Ahora, ¿que pasa si hago esto?

PHP:
  1. $f1 = new Banana(“Pedrito”);
  2. $f2 = new Banana(“Juancito”);
  3.  
  4. $f2 = $f1;
  5. echo $f2->dar_nombre_duenio(); // <<Que imprime acá ¿?

Como dije antes, las variables poseen referencias a los objetos. O sea, los objetos no están dentro de la variable sino que están en otro lado, y solo las variables conocen donde están. Y la única forma de acceder a esos objetos que están flotando por ahí es hacerlo utilizando las variables que conocen a ese objeto.

Cuando asigno $f1 a $f2, lo que estoy haciendo es decirle a $f2 que conozca al objeto que $f1 ya conoce. Entonces las dos variables referencian al mismo objeto. Y el “Que imprime?” mostraría “Pedrito”, dado que el dueño del objeto que $f1 conoce es Pedrito.

El otro objeto, por otro lado, se pierde. $f2 era la única variable que lo conocía y ya no es más referenciado. Por lo tanto no lo puedo seguir usando. Si, por otro lado, otra variable tuviese otra referencia a ese objeto, no lo perderíamos.

¿Que pasa con el objeto que se perdió? No hay que preocuparse. En realidad, PHP (tal como dice la documentación por algún lado) internamente conoce a todos los objetos, y sabe cuando alguien es socialmente inaceptado. A esos objetos que ya ninguna variable referencia, PHP se encarga de eliminarlos y liberar la memoria para que otro objeto mas popular la use.

Ahora, ¿que pasa si quiero que todas las frutas, obligatoriamente sepan o conozcan su dueño?

¿Hago una variable de instancia en cada clase de cada fruta? ¿Qué pasa si tengo 211 frutas, tengo que ir clase por clase y agregar en cada una de ellas una variable de instancia?

¡No! hay una solución más fácil: utilizar herencia de clases.

Todas las frutas son frutas, ¿no? Si. Bueno, entonces lo único que hay que hacer es crear una clase “Fruta” y que todas las clases de frutas “cuelguen” de la clase Fruta. Esto sirve para que todas las frutan “hereden” todas las propiedades y métodos de la clase Fruta. Entonces:

PHP:
  1. class Fruta
  2. {
  3. private $m_duenio = "";
  4.  
  5. function __construct($duenio)
  6. {
  7. $this->m_duenio = $duenio;
  8. }
  9.  
  10. function dar_nombre_duenio()
  11. {
  12. return $this->m_duenio;
  13. }
  14.  
  15. function dar_color_cascara()
  16. {
  17. // YO NO SOY NINGUNA FRUTA EN ESPECIAL !!
  18. return ("Soy una fruta indocumentada y sin personalidad");
  19. }
  20.  
  21. function dar_familia()
  22. {
  23. // YO NO SOY NINGUNA FRUTA EN ESPECIAL !!
  24. return ("Soy una fruta indocumentada y sin personalidad ");
  25. }
  26. }
  27.  
  28. class Banana extends Fruta
  29. {
  30. function __construct($duenio)
  31. {
  32. parent::__construct( $duenio ); // (1)
  33. }
  34.  
  35. function dar_color_cascara()
  36. {
  37. // Yo se que soy una Banana y se que soy amarilla
  38. return "amarillo";
  39. }
  40.  
  41. function dar_familia()
  42. {
  43. return "Musaceae";
  44. }
  45. }
  46.  
  47. $f = new Banana("Raul");

¡Esto se pone cada vez más interesante!

Primero, tengo la clase Fruta, la cual no representa ninguna fruta en particular, y si hago:

PHP:
  1. $f = new Fruta(“asd”);

$f va a ser una fruta inútil e indocumentada, cuyo dueño es “asd”.

(Utilizando interfaces es posible prohibir este tipo de cosas, para más información mirar el manual de PHP)

Segundo, tengo la clase Banana que usando “extends” especializo el comportamiento de la clase Fruta. Se dice que la clase “Banana” hereda de “Fruta”, dado que, en definitiva, una banana es una fruta. Del mismo modo, “Fruta” es una generalización de “Banana”. “Banana” es una “sub-clase” de “Fruta”, y “Fruta” es la “super-clase” de “Banana”.

Banana hereda todo lo que tiene Fruta y, en principio un objeto Banana es igual a una Fruta pero con otro nombre. Entonces para darle cierta personalidad a los objetos Banana, se definen nuevas implementaciones de los métodos que la clase Fruta indica, pero solo aquellos que nos interesan.

Si ahora le pregunto a una banana por el color de su cáscara (“dar_color_cascara”), me la va a decir igual que siempre. Lo mismo ocurre si le pregunto el nombre de su dueño (“dar_nombre_duenio”), la diferencia es que esa función ya no se encuentra mas en la clase Banana sino que ahora está en la clase Fruta.

Felicitaciones, ¡¡acabamos de reutilizar codigo!! De esta forma, cualquier clase que herede de Fruta va a poder conocer a su dueño.

Tener presente que aún sigue siendo necesario indicarle el nombre del dueño al constructor de la clase Banana. En particular, donde dice:

PHP:
  1. function __construct($duenio)
  2. {
  3. parent::__construct( $duenio ); // (1)
  4. }

Estamos construyendo un objeto Banana, y le estamos diciendo al “contructor” de la clase Fruta cual es el nombre del dueño de esa fruta. “parent” representa la “super-clase” de Banana (en este caso, Fruta) y con eso estamos invocando a su constructor.

¿De que me sirve todo esto?

Ahora podemos hablar con cualquier fruta sin conocer realmente que fruta es. Si seguimos caminando por la calle en una oscura noche donde no hay luz y no podemos ver, y nos tropezamos con una fruta, podemos averiguar su dueño, el color de su cascara y de que familia de fruta se trata, sin tener que conocer realmente que fruta tenemos:

PHP:
  1. // $f sigue siendo una fruta desconocida
  2.  
  3. echo $f->dar_nombre_duenio();

No nos importa realmente de que fruta se trata, dado que gracias a la herencia y al usar el mismo nombre en los metodos tenemos un “protocolo” en comun. Ese protocolo se conoce como “interfaz” y, en este caso, se trata de las funciones “dar_nombre_duenio()”, “dar_color_cascara()”, y “dar_familia()”. Toda clase que herede de fruta está obligada a redefinir dichas funciones. Caso contrario, pasaría a ser una fruta indocumentada.

Lo que nos permite no necesitar conocer realmente de que clase particular es un objeto, y solo saber que es una “Fruta”, se conoce como polimorfismo. Gracias al “binding-dinámico” (lo que hace funcionar al polimorfismo), podemos enviarle un mensaje a una fruta cualquiera preguntándole el nombre de su dueño sin importar de que fruta se trata.

Lo que la POO nos facilita es delegar tareas, permitiéndole a cada fruta particular que se comporte como mas le gusta, de acuerdo a su clase, sin tener que conocer a que fruta le estamos hablando. Es lo que se conoce como Abstracción y Ocultamiento. Las frutas son una “caja negra” y no nos interesa saber que hay adentro.

Esto fue una breve introducción a la Programación Orientada a Objetos, y de esto es sobre lo que la POO trata. Existen muchas cosas que NO son ciertas sobre POO: el uso de sobrecarga de funciones NO ES polimorfismo por si solo. La sobrecarga de funciones es tener varias de ellas con el mismo nombre. Esto no tiene nada de polimórfico ya que uno invoca a una función concreta; uno no puede identificarla por el nombre pero si por los parámetros.

Hay muchas otras cosas en el tema, Double-dispatching y Template Method son los siguientes escalones que normalmente se aprenden. Si encuentro algo de tiempo, escribiré sobre ellos.

Para que se hagan una idea, hay casos en los que es necesario conocer la clase de un objeto, por ejemplo si tenemos distintos animales que comen distintas frutas de distintas formas. El orangután por ejemplo se come la banana sin pelarle la cáscara, mientras que una jirafa la lava, la seca, la pela y le unta un poco de mayonesa. Para hacer eso, cada animal que come las distintas frutas debe conocer que fruta está comiendo.

Para resolver ello, hay dos opciones: en PHP hay una funcion que dado una instancia de un objeto devuelve su nombre. La otra opcion es usar Double Dispatching. No se olviden que cada clase conoce de que clase realmente es. Y por favor, no usen strings!.

Saludos,
Alejandro “Alejo”.

Lapiz y Papel

Thursday, August 24th, 2006

Necesito hacer diagramas (al estilo UML) pero no quiero hacerlos en lapiz y papel! estoy encaprichado. Alguien sabe alguna forma de hacerlos en una computadora y que sea tan facil como hacerlos en lapiz y papel?

La respuesta es facil:

  • comprarse una tablet-pc o una palm de las nuevas con touch.screen, o algun monitor con touch.screen, o algo similar.

¿ y sin tener que gastar plata en ello ?

  • usando lapiz y papel y cada cierto tiempo digitalizarlo usando scanner, webcam o camara digital
  • usando una pizarra y cada cierto tiempo digitalizarlo usando webcam o camara digital

O mejor aun, la mejor opcion es no hacer los diagramas. Lamentablemente no es realista.

Mucho lio, me quedo con el lapiz y papel (que dicho sea de paso, no tengo)

La Lista De Schindler

Thursday, August 17th, 2006

Somos Programadores

Tuesday, August 15th, 2006

miren este video, muyyy bueno realmente:

http://www.youtube.com/watch?v=9Iozx-6XymE

Deduccion Pura

Monday, August 14th, 2006

1. Dios es amor.
El amor es ciego.
Stevie Wonder es ciego.
Luego, Stevie Wonder es Dios.

2. Me dijeron que no soy nadie.
Nadie es perfecto.
Luego, yo soy perfecto.
Pero sólo Dios es perfecto.
Por tanto, yo soy Dios.
Si Stevie Wonder es Dios, yo soy Stevie Wonder
¡¡¡¡ A la mierda, soy ciego!!!!

3. "A quien madruga Dios le ayuda"
Quien madruga, duerme por la tarde...
Quien duerme por la tarde, no duerme por la noche...
Quien no duerme por la noche, sale de fiesta.
Conclusión: "A quien sale de fiesta Dios le ayuda".

4. Si yo soy Dios, y por tanto ciego, y Dios ayuda a quien sale por la noche, y yo salgo por la noche, entonces yo me ayudo a mí mismo,
pero ¿cómo carajo sé que es de noche si soy ciego?

Deduccion pura.