POO FOR AJARES
Hay una cosa que todo programador vocacional teme (en realidad hay más de una): explicar la programación orientada a objetos (POO) a alguien lego en la materia o como mucho con nociones básicas. A mí me ha sucedido en varias ocasiones, y siempre he fracasado estrepitosamente. La última vez ha sido recientemente y me ha dolido mucho porque ha sido ante Mr. Pink quien previamente nos había explicado cómo el déficit de tarifa es un invento de las grandes eléctricas para joder además de a los usuarios, a las pequeñas, principalmente a las renovables, quien además de no ver un duro cargan con la mala fama.
Así pues voy a intentarlo una vez más, pero ésta vez por escrito, lo cual tiene la ventaja de que si me equivoco podéis corregirme.
Empecemos diciendo que la programación procedural es la programación de toda la vida. Lo primero que uno piensa cuando se plantea cómo solucionar problemas mediante un ordenador: instrucciones haciendo cosas, una detrás de otra, con ocasionales bifurcaciones y poco más. Para lograr esto sin tener que repetir código creamos procedimientos que hagan cosas muy concretas y puedan ser llamados varias veces desde diversos puntos. El flujo del trabajo es bien conocido.
La mayoría de los programadores que conozco empezamos a programar en una pizarra con símbolos parecidos a éstos. Por cierto ¿Adivinais que calcula este programa? Una pista, tiene un bug, o al menos un caso no contemplado.
La programación orientada a objetos se organiza de otra manera, que frecuentemente impide conocer de antemano el flujo de ejecución que seguirá nuestro programa, pero que tiene otras ventajas que más adelante veremos.
Ambos estilos son complementarios, hay problemas que se resuelven mejor de una manera y otros de la otra. Está matemáticamente demostrado que todo lo que se puede hacer en un estilo puede hacerse con el otro, e internamente al ordenador le importa un pimiento el método escogido, pues él solo sabe ejecutar cosas una detrás de otra, hacer comparaciones y saltar a una instrucción u otra según dichas comparaciones.
Para tratar de ilustrar las diferencias entre ambas formas de programación voy a imaginar cómo se haría un Lemmings (o al menos una pequeña parte) mediante una y otra filosofía. Si hay algun verdadero programador de videojuegos en la sala espero sepa perdonar mis simplificaciones y errores, que son en aras de una mayor claridad… y porque no tengo ni puta idea de como programar uno que se mueva en tiempo real, todas las cutreces que he hecho en mi vida iban por turnos.
Con éste sencillo curso iniciamos nuestro camino a ser la próxima Rockstar.
Procedural Style:
En un Lemmings lo más importante es llevar un control de los mismos, para ello definiremos una estructura de datos, que contenga su posición y la dirección en la que camina. Esta estructura de datos la definiremos una vez en el código fuente, lo llamaremos RegistroLemming, pero a la hora de ejecutar el programa está claro que necesitaremos tantos de estos registros en memoria como Lemmings tengamos. Es MUY importante entender esto: una cosa es la definición de un registro, que solo lo hacemos una vez por tipo de registro, y otra la cantidad de esos registros que tendremos que meter en memoria para que nuestro programa funcione, que varían de 0 a N. Siendo en este caso N el nº de Lemmings.
Ahora que tenemos localizados a los Lemmings toca hacer la parte importante del programa, moverlos y pintarlos en pantalla, para ello escribiríamos un procedimiento consistente en un bucle infinito mientras nos queden Lemmings en la pantalla (recordemos la tendencia a morirse de estos bichitos) que fuera lemming por lemming actualizando su posición, comprobando si ha chocado contra una pared para cambiar la dirección en la que camina, y pintándolo de algún modo en pantalla. Este bucle tiene que ser lo suficientemente rápido como para ejecutarse pongamos 60 veces por segundo, y así conseguiríamos unos estupendos 60 FPS.
Si programais vuestro primer juego por entero sin usar utilidades como Game Maker y similares, aconsejo que los gráficos sean de este estilo. Y aun así se relentizará.
Ya tenemos el esqueleto de nuestro programa y no ha sido demasiado difícil de hacer, veamos ahora como lo haría yo mediante programación orientada a objetos.
Object Style:
En la programación orientada a objetos el concepto fundamental es la Clase. Habrá una clase por cada tipo de objeto que debamos crear, en este caso tendremos la Clase Lemming, que además de la posición y la orientación de los bichitos contendrá una serie de Métodos que permitirá que cada Lemming goce de cierta autonomía. Al igual que en caso anterior una cosa es la Clase Lemming y otra los 80 Lemmings que deberemos crear para representarlos en el juego, los llamaremos Instancias de la Clase Lemming. Uno de los métodos de la Clase será “desplaza”, que permitirá que sus instancias actualicen su posición de acuerdo a su orientación (recordemos que habrá tantas instancias como bichitos, y cada uno tendrá su propia posición y dirección, totalmente diferente de la de los demás). Otro método será “pinta”, que de alguna forma dibujará el Lemming en pantalla.
Como se ve el código que hemos escrito es prácticamente el mismo solo que está organizado de otra manera ¿Qué hemos ganado entonces? Además ¿Cómo sabe un Lemming cuando tiene que moverse o cuando ha chocado con una pared?
Eso es porque aún no hemos acabado. Un programa rara vez está compuesto de un único tipo de Objeto, en este caso necesitamos más, al menos una Clase llamada Reloj que haga “tick” 60 veces por segundo, y una Clase Escenario, que además de muchas otras cosas sea capaz de indicar si una posición es una pared o no.
Lo importante de la orientación a objetos es que los objetos son capaces de relacionarse unos con otros, y escucharse unos a otros. Por ejemplo todos nuestros Lemmings escucharían al reloj, y al recibir un tick se desplazarían y pintarían. ¿En qué orden? Antes íbamos del primero al último, ahora no está claro, y lo cierto es que para nuestro caso no nos importa, lo único que queremos es que lo hagan todos. SI hay que seguir un orden determinado tendremos que programar algo que lo fuerce, pero para el ejemplo no es necesario. A su vez cada Lemming al desplazarse debe preguntar al Escenario, si su nueva posición es una pared, y si es así cambiar el sentido de su marcha.
La POO tiene su propio set de diagramas llamado UML, siendo el diagrama de Clases el más conocido. Supongo que en los departamentos serios de informática lo usaran. No es mi caso. Tambien es posible que esté completamente desfasado.
Nuestro programa ya está completo, pero no solo no hemos escrito menos código, sino que seguramente lo hemos aumentado. ¿Qué hemos logrado? Pues tenemos una Clase Lemming casi independiente, podremos usarlo en otro programa siempre que dicho programa cuente también con un Reloj y un Escenario. Podemos usar estos Lemmings como extras de un escenario del Street Fighter 2 si nos apetece.
Otra ventaja que tiene la POO es que las Clases gozan de una cosa que se llama Herencia. A grandes rasgos permite que una Clase herede de otra toda su funcionalidad, y que además le añada alguna más. Como la clase hija es capaz de hacer todo lo que haga su padre en teoría debería poder ser usada en cualquier sitio donde encajara dicho padre.
Por ejemplo, supongamos que estamos en los albores de la informática siendo pioneros con el Lemmings, los ordenadores no tienen sonido y nuestro programa es un éxito. De repente aparecen las tarjetas de sonido y sin embargo nuestro programa es completamente mudo. Decidimos sacar un parche que lo solucione, pero no podemos perder la compatibilidad con los ordenadores que no tengan ese adelanto. De la música ya nos ocuparemos, de momento nos centraremos en que suene un efecto sonoro cada vez que un lemming choque contra una pared (o se muera). Para ello crearemos una clase LemmingSonoro, que heredará de Lemming, y que a su vez redefinirá el método que cambia su orientación para que cada vez que suceda suene el efecto. Como puede verse mediante herencia no solo podemos añadir funcionalidad, sino que además podemos modificar la que ya hubiera. Cuando el programa arranque comprobará si el ordenador tiene tarjeta de sonido, y si la tiene creara 80 LemmingSonoro y si no 80 Lemming normales, y todo funcionará como la seda.
Como se ve la POO es una forma diferente de estructurar el código que permite la reutilización del mismo. Pero para ello ha de estar muy bien diseñado, cada Clase debe tener una función específica y solo una, ya que cuando empieza a hacer cosas de más y aumenta su dependencia deja de ser tan reusable. Esta manera de programar nos evita esos tochos de código en los que se un bucle tiene que ocuparse de todo como teníamos en el primer ejemplo, pero a costa de perder un poco la trazabilidad. En el momento en que nuestros objetos empiezan a depender unos de otros, a “comunicarse” entre sí, es como si cobrasen vida y el orden de ejecución deja de estar tan claro.
En cuanto el programa se complica un poco esta reacción es de lo mas habitual.
Como he dicho no hay un estilo superior a otro, aunque es cierto que la POO siempre es la elegida cuando hay alternativa entre escoger uno u otro, dado que la especialización de las Clases en una única tarea permite escribir código más mantenible en el tiempo, es decir, más fácil de entender y modificar llegado el caso, algo fundamental en cualquier proyecto.
En el siguiente artículo de esta serie, si es que interesa, hablaré de los diferentes lenguajes de programación y porqué algunos son más rápidos que otros y en definitiva, de por qué el puto Minecraft no va bien en mi ordenador.