16 diciembre 2005

¿Qué son los patrones de diseño?. El patrón Observador

Llevo algún tiempo usando patrones de diseño y la verdad, es algo interesante y que merece la pena aprender cuando se anda metido en proyectos de programación un poco en serio.

Uno de los principales problemas que veo con todo este tema de patrones de diseño es que no hay ningún sitio que explique de forma clara qué es un patrón de diseño a una persona que NO tenga ni idea de lo que es un patrón de diseño. Las definiciones que se dan de un patrón son demasiado abstractas y utilizan palabrarería demasiado formal. El resultado es que si no sabes lo que es un patrón de diseño, puedes interpretar esas definiciones como te de la gana.

Por ello pretendo dar aquí, más que una definición, una explicación informal de forma que alguien que no sepa qué es un patrón de diseño sepa, al menos, de qué va. Lo haré con un ejemplo.

Normalmente cuando nos dedicamos a codificar hay pequeños trozos de código que hacemos, a veces sin pensar en ellos porque nos resultan evidentes, otras veces estrujándonos los sesos porque no son tan evidentes. Si ese código sólo nos sirve para ese programa, todo va más o menos bien.

Sin embargo, es normal que luego tengamos que hacer otro programa similar. Es bastante normal también que tengamos que copiar el primer programa o trozos del mismo en otro sitio y empezar a modificar, porque el código que tenemos que hacer se parece al que teníamos, pero no es exactamente igual.

Cuando un programador se ve obligado a hacer eso una y otra vez, a copiar y pegar el mismo código y modificarlo una y otra vez, empieza a pensar en la forma de hacer ese trozo de código de forma que la tarea de llevárselo a otro sitio sea más cómoda para él. El programador empieza a inventar diferentes formas de hacer ese trozo de código de forma que sea lo suficientemente versátil y configurable como para no tener que volver a tocarlo nunca más. Idealmente, le debería bastar con llevarse la librería ya compilada de un programa a otro.

Ha habido en la historia miles de programadores con este problema y miles de ellos han llegado a las mismas soluciones o formas de hacer esos trozos de código de forma que sean completamente reutilizables. Esas soluciones son los "patrones de diseño" y en su día "la banda de los cuatro" (cuatro gurús de la programación) los recopilaron en un libro.

Los patrones de diseño nos cuentan cómo hacer nuestro código en esas pequeñas cosas, de forma que nuestro código sea reutilizable, se pueda cambiar cómo se hacen las cosas, ampliar su funcionalidad ... ¡¡ Sin necesidad de tocar ese código !!.

Vamos con un ejemplo para ver qué quiero decir. Supongamos que en nuestro programa nos llega un dato de algún sitio (pongamos un número, un 3) y debemos guardarnos ese dato junto con otros que nos llegaron anteriormente, pintarlos en un gráfico, mostrarlos en una ventana que tiene una lista de datos y además imprimirlo.

Nos ponemos a ello. Nos hacemos nuestro array/lista para añadir el dato junto a otros que nos han llegado anteriormente. Luego nos hacemos unas clases grafico, ventanaConLista e impresora a las que ponemos el método tomaNuevoDato(int) para que hagan lo que tiene que hacer con el dato.

El código nos puede quedar más o menos así (supongamos que dato es el nuevo dato que nos ha llegado).

array[i] = dato;
grafico.tomaNuevoDato(dato);
ventanaConLista.tomaNuevoDato(dato);
impresora.tomaNuevoDato(dato);

Bien, todo esto está estupendo y parece correcto. Además, no hemos tenido que pensar demasiado en cómo repartir el dato a los distintos sitios. Si nadie nos pide modificar el código o extraer un trozo de él para usarlo en otro sitio, todo está bien.

Vamos ahora a empezar a sacarle pegas a este código.


Para pasar el dato a alguien más, hay que añadir líneas a este código.

Si ahora nos piden que el dato se envíe también a través de un socket, tenemos que hacernos nuestra clase MiSocket, ponerle el método tomaNuevoDato() y llamarlo. En el código anterior, hay que añadir una nueva línea.

miSocket.tomaNuevoDato(dato);

Estaría mucho mejor si este código ya hecho NO tuvieramos que editarlo, recompilarlo y quizás incluso depurarlo con debugger.

Este trozo de código depende de muchas cosas

Este trozo de código (y la clase que lo contiene) debe ver a las clases Grafico, VentanaConLista, Impresora y ahora MiSocket. Este código (ni esta clase) no funciona si no se dispone de todas las demás clase. Como además contiene el array de datos, cualquier otro que necesite el array de datos y se lo pida a esta clase, también necesita ver (de forma indirecta) a esas clases de Grafico, VentanaConLista..., etc.


Todos estos problemas no son problemas para la gente que empieza a programar o hace proyectos totalmente distintos unos de otros. Este problema lo encuentra la gente que luego hace un segundo proyecto muy similar, tiene que copiar este código, editarlo, quitar por ejemplo el gráfico porque en su siguiente proyecto no hay gráfico y añadir un Plotter (ahora quieren pintar el dato que llega en un Plotter).

Los patrones de diseño nos enseñan una solución al problema de repartir el dato a varios sitios, de forma que el código que reparte el dato NO hay que cambiarlo. El patrón de diseño que nos permite hacer esto se llama Observador (Observer pa los extranjeros).

¿Cómo se hace eso con el patrón Observador?. El patrón Observador nos dice que hagamos una interface con el métdo tomaNuevoDato(), así

public interface ObservadorNuevoDato
{
public void tomaNuevoDato(int nuevoDato);
}

Nos dice luego, que nuestras clases Grafico, VentanaConLista, etc implementen esa interface (cosas que ya están haciendo, puesto que tienen el método tomaNuevoDato()).

public class Grafico implements ObservadorNuevoDato
{
public void tomaNuevoDato (int nuevoDato)
{
this.pintoElDato(nuevoDato);
}
}

y ahora viene lo realmente interesante. Debemos hacernos una clase que tenga dentro el array de datos, que tenga una lista de ObservadorNuevoDato y que admita añadir y borrar ObservadorNuevoDato de esa lista. Cada vez que llegue un nuevo dato, debe recorrer la lista de Observadores y avisarles. Algo como esto

public class MisDatos
{
protected int [] array;
protected LinkedList observadores;
public MisDatos()
{
...
}
public void anhadeObservador(ObservadorNuevoDato observador)
{
observadores.add(observador);
}
public void tomaNuevoDato (int nuevoDato)
{
array[i] = nuevoDato;
for (int j=0;j<observadores.size();j++)
((ObservadorNuevoDato)observadores.get(j)).tomaNuevoDato(nuevoDato);
}
public void borraObservador (...)
{
...
}
}

Bueno, esto es lo mismo que teníamos pero más complicado. Sin embargo nos ahorra los dos problemas anteriores.

Esta clase NO es necesario volver a tocarla. Admite que le pongamos Observadores a nuestro gusto.

Esta clase NO depende de nadie (sólo de la interface). Podemos llevarnos la clase y la interface a cualquier otro proyecto sin necesidad siquiera de recompilarla, nos podemos llevar el .jar donde esté sin necesidad de llevarnos el fuente.

Obviamente, esta ventajas son mucho más ventajosas si los datos son algo más complicado que un array de enteros y la clase es algo más que una clase con un array dentro...

10 comentarios:

BadBoy dijo...

Buena la pagina compadre, rapido, sencillo e indoloro ... Felicitaciones!!

nullPointer dijo...

página oficialmente marcada como favorita, sigue así :)

thunder dijo...

Excelente la explicación, me ha quedado muy claro el tema, felicitaciones!!!

SilverKeni dijo...

muy bien amigo
por la deifinicion del tema
felicidades

SilverKeni dijo...

Muy Bien Amigo No Tenia Idea Del Tema, Ahora Lo comprendo muy Bien..

felicitaciones por la Explicaion
..
sigue a si

atte. tu amigo
Keni

Michel de Argentina dijo...

Simple y claro. Te lo agradezco.

Anónimo dijo...

Excelente explicación sobre el patrón del Observador, nomás te faltó agregar la forma en que será implementada!!

Saludos

Anónimo dijo...

wena explicacion

Anónimo dijo...

Lo MaxiMo!!!!....EsTo es Lo MeJor Q EnConTre de PaTroNeS!!!!! MuChAs FeLiCiDaDes Y SiGue comParTiEnDo Tu SabiDuRia cOn NoSoTroS AqueLLos Q SoMos CaChoRRoS En ToDo EstO!!!!!

Anónimo dijo...

muy buena!!.. me ayudo bastante en una exposicion!