« Un ejemplo el patrón observer (la versión actionScript) | Inicio | Resumen de novedades del .Net framework 2.0 »

Un ejemplo del patrón observer (la versión Java)

Está desencadenado. El primer ataque ha sido lanzado. El profesor Dispar ha dado las órdenes a sus huestes para dominar el mundo. En post anteriores hemos visto como el profesor Dispar ha conseguido clonar cualquier animal ( con gran predilección por ovejas y vacas ) utilizando un patrón prototype, ha conseguido darlas un rol dinámicamente con el patrón extension objects, y ha repartido las órdenes con un patrón command.

Pero como ya sabemos, el profesor Dispar está loco, pero no es idiota. Sabe, que algo puede salir mal, que un pequeño detalle puede truncar sus planes de dominar el mundo. Y también sabe que una retirada a tiempo es una victoria.

Está desencadenado. El primer ataque ha sido lanzado. El profesor Dispar ha dado las órdenes a sus huestes para dominar el mundo. En post anteriores hemos visto como el profesor Dispar ha conseguido clonar cualquier animal ( con gran predilección por ovejas y vacas ) utilizando un patrón prototype, ha conseguido darlas un rol dinámicamente con el patrón extension objects, y ha repartido las órdenes con un patrón command.

Pero como ya sabemos, el profesor Dispar está loco, pero no es idiota. Sabe, que algo puede salir mal, que un pequeño detalle puede truncar sus planes de dominar el mundo. Y también sabe que una retirada a tiempo es una victoria.

Así, pues, ha equipado a sus huestes de una radioenigma de campaña ( es decir, una radio acompañada de una máquina enigma ). Pero, ¿ porqué ha hecho esto ?. Sencillo, en el fragor de la batalla, las comunicaciones directas se hacen complicadas. Es difícil hacer llegar las órdenes, y muchas veces no se tiene claro a quien le estamos dando esas órdenes ( ¿será una vaca?, ¿ será una oveja? ).Conquistar el mundo no es fácil, de hecho muchos lo han intentado pero no lo han conseguido. El profesor Dispar, ha estudiado con detenimiento todas las intentivas anteriores para no reproducir los mismos errores. ¿ Qué pasaría si para dar nuevas órdenes, el profesor necesitase que el sargento vaca tuviese que recorrerse la trinchera indicando una por una a todas las vacas y ovejas que se retirasen ?. Desde que le diese la orden a la primera, hasta que se la diese a la última, pasaría un tiempo precioso. Además, ¿ y si ocurre algo por el camino ?. Todos hemos jugado al fútbol de pequeños después de comer, y sabemos lo que ocurre cuando te pones a correr. Se llama flato. Si nos pasa a nosotros que tenemos un estómago, imaginemos las posibilidades que hay de que le pase al sargento vaca que tiene 4. Las posibilidades de que las órdenes no lleguen a todos los integrantes de nuestra tropa son amplias.

Sabiendo esto, el profesor Dispar, que está loco pero no es idiota, para poder modificar ( actualizar ) fácil y rápidamente el comportamiento de sus huestes, utilizará el patrón Observer.

Como el profesor Dispar no es idiota ( aunque este loco y sea un genio del mal, no es idiota ), ha encontrado una malvada forma de enviar las órdenes a sus tropas. Las ovejas y vacas, estarán equipadas con una radio que sintoniza Cadena Dial ( sí, entendemos que es desagradable, pero dominar el mundo tiene estas cosas ). Sus tropas estarán atentas a la radio, y cuando esta emita una canción de Village People, la máquina enigma les indicará qué acción deben tomar.

enigmaBroadcaster.jpg
Dj Dispar emitiendo su programa

El código será algo así

public interface IObserver { public void update(Message info); }

public interface ISubject { public void addObserver(IObserver obs); public void removeObserver( IObserver obs ); public void notifyObserver( ); }

public class Cow implements IObserver { public Cow( ) { debug("Soy una vaca muuuuuu| I am a cow Muuuu"); } private void debug( String arg ) { System.out.println("Vaca | Cow :-> "+arg ); } public void update( Message msg ) { debug("Actualizada | update "); String kk = msg.getMsg(); debug("msgText: "+kk ); } }

public class Sheep implements IObserver { public Sheep( ) { debug("creada la oveja | a new sheep"); } private void debug( String arg ) { System.out.println("Oveja |Sheep :-> "+arg ); } public void update( Message msg ) { debug("Actualizada | update "); String kk = msg.getMsg(); debug("msgText: "+kk ); } }

El mensaje:

public class Message { String msg = ""; public void setMsg( String arg ) { this.msg = arg; } public String getMsg( ) { return this.msg; } }

public class Profesor implements ISubject { private ArrayList misObservers; private Message miMsg; public Profesor( ) { //System.out.println("profesor profesor"); debug("Creando al profesor | a new Profesor"); misObservers = new ArrayList(); } public void debug( String arg ) { System.out.println("Profesor:-> "+arg); } public void addObserver( IObserver obs ) { debug("añadido observer | a new observer"); misObservers.add(obs); debug("Ya tengo | I have: "+misObservers.size()+" observers "); } public void removeObserver( IObserver obs) { debug("eliminado observer | removing observer"); //int value = indexOf misObservers.remove(misObservers.indexOf(obs)); debug("ahora me quedan | now i have : "+misObservers.size()+" observers "); } public void notifyObserver( ) { debug("notificar observers | notify observer "); miMsg = new Message( ); miMsg.setMsg("In the Navy"); for( int i = 0; i < misObservers.size(); i++ ) { IObserver obs = ( IObserver )misObservers.get(i); obs.update( miMsg ); } } }

Y para finalizar

public class ObserverSample { public static void main(String[] args) { Profesor prof = new Profesor(); Sheep oveja = new Sheep(); Cow vaca = new Cow(); Sheep oveja2 = new Sheep( ); Cow vaca2 = new Cow( ); Sheep oveja3 = new Sheep(); Cow vaca3 = new Cow(); prof.addObserver( oveja ); prof.addObserver( oveja2 ); prof.addObserver( oveja3 ); prof.addObserver( vaca ); prof.addObserver( vaca2 ); prof.addObserver( vaca3 ); prof.removeObserver(oveja); prof.notifyObserver( ); } }

De este modo, el profesor, puede comunicar rápidamente con todas sus tropas. Es brillante, es genial, es maravilloso, es....¿ perfecto ?. Bueno, la verdad es que está muy bien, pero imaginemos, por un momento una situación en la que una oveja consigue infiltrarse en el alto mando enemigo, y descubre que los enemigos están a punto de ser derrotados, y en ese momento de extasis, la radio emite "In the Navy" indicando a las tropas que hay que retirarse. NOOOOOOOOO ( bueno, esto está traducido, claramente la oveja diría algo como BEEEEEEEEEEE ). Nuestra oveja espía, tiene una información valiosísima, pero que no puede comunicar a su alto mando, porque la forma de transmisión es únicamente de tipo push, del profesor a sus tropas, no hay retorno. Que situación, la oveja necesita transmitir que el ataque debe continuar pero....

enigmaReceiver.jpg
Una oveja tarareando "In the Navy"

ajajajajajajajaj, el profesor Dispar ( loco pero no tonto ) ha pensado en ello, y ha decidido implementar un patrón observer que permita la transmisión tipo push ( del sujeto emisor a todos los sujetos receptores ) y tipo pull ( uno de los objetos receptores, puede solicitar una información al objeto emisor para que este la envíe a todos los demás ). Así, nuestra oveja espía, estará equipada de un número de teléfono al que podrán llamar y solicitar que emitan "Rasputin" de Boney M, y cuando esta canción sea emitida por la radio, las ovejas y vacas que la escuchen, sabrán que ha llegado el momento de la ofensiva final. JAJAJAJAJAJAJAJA. Para ello debemos modificar ligeramente nuestros códigos anteriores.

Para empezar nuestro sujeto, implementará un nuevo método que hemos añadido a la interfaz ISubject( requestInfo ), que permitirá recibir las llamadas telefónicas de la oveja espía.Vemos también que el constructor de las ovejas y las vacas también ha cambiado un poco. Ahora las ovejas y vacas, almacenarán una referencia al constructor. También hemos modificado el código de nuestra oveja ( suponemos que esta es la oveja espía en el momento en que decide que pese a lo que emite la radio, hay que atacar y pide que se emita Rasputin que es otra señal de ataque ).

Por tanto, la interfaz ISubject quedará así:

public interface ISubject { public void addObserver(IObserver obs); public void removeObserver( IObserver obs ); public void notifyObserver( Message msg ); public void requestInfo( String arg ); }

public class Cow implements IObserver { public Cow( ) { debug("Soy una vaca | I am a cow "); } private void debug( String arg ) { System.out.println("Vaca | Cow :-> "+arg ); } public void update( Message info ) { debug("Actualizada | update "); debug(" -> "+ info.getMsg()); } }

public class Sheep implements IObserver { private ISubject profesor; public Sheep(ISubject isub ) { debug("creada la oveja | a new Sheep"); this.profesor = isub; this.profesor.addObserver(this); this.profesor.requestInfo( "Rasputin" ); } private void debug( String arg ) { System.out.println("Oveja | Sheep:-> "+arg ); } public void update( Message info ) { debug("Actualizándome | update"); debug( " -> "+info.getMsg()); } }

import java.util.ArrayList; public class Profesor implements ISubject { private ArrayList misObservers; public Profesor( ) { //System.out.println("profesor profesor"); debug("Creando al profesor | a new profesor"); misObservers = new ArrayList(); } public void debug( String arg ) { System.out.println("Profesor :-> "+arg); } public void addObserver( IObserver obs ) { debug("aÒadido observer | a new observer"); misObservers.add(obs); debug("Ya tengo | I have: "+misObservers.size()+" observers "); } public void removeObserver( IObserver obs) { debug("eliminado observer | removing observer"); misObservers.remove(misObservers.indexOf(obs)); debug("ahora me quedan | now i have: "+misObservers.size()+" observers "); } public void notifyObserver(Message msg ) { debug("notificar observers | notify observers"); for( int i = 0; i < misObservers.size(); i++ ) { IObserver obs = ( IObserver )misObservers.get(i); obs.update( msg ); } } public void requestInfo( String arg ) { debug("recibiendo una petición | someone has asked for ..."); Message msg = new Message( ); msg.setMsg( arg ); this.notifyObserver( msg ); } }

Y por fin:

public class ObserverSample { public static void main(String[] args) { Profesor dispar = new Profesor(); Sheep oveja = new Sheep( dispar ); } }

Evidentemente, el profesor, que ha estudiado la API de JAVA, sabe que existe la clase java.util.Observable y la interface java.util.Observer. La primera no la ha utilizado, pues eso implicaría que el profesor ( el mismo ) extiende o hereda de Observable. Esto podría ser posible, pero en realidad el profesor extiende de una larga extirpe de profesores diabólicos empeñados en conquistar el mundo, y ni quiere ni puede extender de Observable. ( class Profesor extends ExtirpeDeProfesores, Observable -> no es posible ) Por eso ha decidido construirse su propia interface ISubject. Con respecto a la interface java.util.Observer, sí la podría haber utilizado para que sus vacas y ovejas la implementasen en lugar de implementar IObserver, pero ya saben, los genios del mal, ellos se lo guisan y ellos se lo comen.

Los códigos están simplificados al máximo simplemente para ilustrar como es el patrón. Una vez más comprobamos que el profesor tiene sus planes muy bien estudiados, no es idiota ni tonto. Sólo ¿ está loco ?.....jajajajajajajajajaja ( ya saben, risas de ultratumba con el fundido a negro y el cartel de the end )

Si quieres, puedes bajar el código fuente aquí

Comentarios

Excelente como siempre... didactico y creativo. Buen punto eso de incluir el código fuente.

Gracias