Un ejemplo del patrón memento ( la versión java )
Conquistar el mundo no es fácil. Nada fácil. Yo lo sé, tú lo sabes, incluso el Profesor Dispar lo sabe.
El Profesor se siente preparado para llevar a cabo su malvado plan. Tiene el conocimiento teóricos, tiene los conocimientos prácticos, tiene un plan, tiene hasta unas gafas de sol nuevas, pero ¡hay tantos detalles que pulir antes de lanzarse a la conquista del mundo!.
En episodios anteriores, hemos visto cómo el Profesor ha implementado el patrón prototype ( para crear su ejército de clones -¡anda, acabo de caer!- ), el patrón extensión objects ( para asignarles sus roles ), el patrón command ( para asignarles las órdenes ), y el patrón observer ( para implementar el sistema de comunicaciones ). Parece que el Profesor Dispar ha estado bastante ocupado implementando patrones, pero ha sido suficiente?. NO!! ( muhahahahahahhaha ).
Si recuerdas el post sobre el patrón observer ( cosa que dudamos mucho, porque nosotros ya no nos acordamos ), dejamos a las vacas y las ovejas en el instante previo al ataque final ( mientras el Profesor reía como un histérico ). Están esperando ( insértese aquí música heroica, por favor, en dramático crescendo ) a recibir la orden de ataque. Cada vaca, cada oveja, está alerta, escuchando la radio, esperando escuchar la señal secreta, para abandonar su posición y lanzarse a cumplir las órdenes recibidas.
El Profesor Dispar está a punto de presionar el botón de "atacar", cuando de repente se da cuenta de que algo no está bien ( y la música heroica se apaga ). “¿Qué pasa si me veo obligado a dar la orden de retirada a mis tropas?. No es que mi plan vaya a fallar ( después de todo soy un genio del mal ), pero ya se sabe, no se puede confiar en los subordinados, y qué pasa si tengo que cancelar el ataque cuando mis huestes ya han comenzado a avanzar ( glorioso avance, por supuesto )?”.
Ciertamente, el Profesor Dispar es un genio. Se ha dado cuenta de un sutil “bug” en su plan. ¿Qué pasa si tiene que dar la orden de cancelar el ataque cuando sus tropas ya han abandonado su posición inicial?. Bueno, ha implementado un mecanismo de comunicaciones, así que puede mandar la señal secreta de retirada, ¿no?. ( muhahahahahah, ya sabes ). Pero hay un problema: la vacas y las ovejas son conocidas por su notoria falta de memoria ( peor incluso que los peces ). Claro, que también son conocidas por la calidad de los quesos que se producen con su leche, pero eso está fuera del alcance de este tutorial.
La cuestión es complicada. Una vaca no tiene memoria. Una oveja no tiene memoria. Punto-pelota. Sólo pueden recordar una cosa a la vez. Por tanto, pueden recordar que tienen que atacar, que tienen que moverse hacia algún lugar, pero en cuanto se meten esa información en la mollera no son capaces de recordar nada más ( como, por ejemplo, dónde estaban cinco minutos antes, o si le habían pedido dinero prestado a alguien ).
Dicho de otra forma, el Profesor Dispar les puede decir que vuelvan a su posición original, pero eso no va a servir de nada, porque no se acuerdan de cuál era su posición original.
Peeeeeero ( y vuelve a empezar la música heroica ), el Profesor Dispar recuerda vagamente, como entre una nebulosa, sus tiempos mozos de estudiante, cuando en su clase explicaron el patrón memento.
¿Qué pasaría si cada oveja y cada vaca fuera capaz de escribir en un cuaderno ( cada una en su cuaderno, será por dinero… ) su posición inicial, y entregara ese cuaderno a su Sargento?. ¿Y si el Sargento guardara esos cuadernos, y se los entregara a sus propietarios si los propietarios tuvieran que volver a las posiciones iniciales?. ¡Problema resuelto! ( muhahahahahahah ). ¡Es perfecto!. Las ovejas y las vacas sólo tendrán que recordar una posición ( la posición hacia la que se supone que tiene que ir, sin importar si están atacando o están en retirada ), mientras una entidad externa les guardará la información que necesitarán para retirarse.
¿Pero cómo puede la oveja guardar su información relevante en un cuaderno?. Fácil ( aparte de las limitaciones fisiológicas propias de su condición ). Cada oveja será responsable de crear una instancia de la clase en la que se va a guardar su información interna, y la entregará a su sargento.
package sheep;
public class SheepMemento
{
private int serialNumber;
private String location;
public SheepMemento( int serial, String loc )
{
serialNumber = serial;
location = loc;
}
public setLocation( String newLocation )
{
location = newLocation;
}
public String getLocation( )
{
return location;
}
}
¡Un momento!. El Profesor se ha dado cuenta de que hay un punto débil en su plan. Si el Sargento guarda los cuadernos con las posiciones iniciales de las ovejas, hay riesgos de seguridad. ¿Qué pasa si los pesados del enemigo roban esa información?. ¡¡O incluso peor!!. ¿Y si los pesados del enemigo no roban esa información, pero la sustituyen por información falsa?. ¡Un momento!. Si la información contenida en los cuadernos estuviera encriptada, o sólo fuera accesible por las ovejas, ¡el problema estaría resuelto, nadie podría cambiarla!.
Por tanto, para evitar posibles cambios en esa información, el Profesor va a tomar dos medidas drásticas. Por un lado, todos los valores guardados en la clase SheepMemento sólo se podrán asignar a través del constructor. De ese modo, el Profesor se asegura que sólo se podrán asignar al crear la clase, y nunca después.
package sheep;
public class SheepMemento
{
private int serialNumber;
private String location;
public SheepMemento( int serial, String loc )
{
serialNumber = serial;
location = loc;
}
}
Pero desgraciadamente, no es suficiente ( es tan difícil conseguir un buen plan ). El Profesor Dispar quiere evitar que la información guardada en el memento sea modificada. De hecho, quiere que sólo sea una oveja la que pueda crear su memento correspondiente, y asignar sus valores. De esa forma, puede asegurarse de que nadie modifique esa información.
Hacer eso en Java es bastante fácil. Basta con colocar la clase SheepMemento en el mismo package que la clase Sheep, y hacer tanto su constructor como sus variables de clase protected. De esa forma, sólo pueden crear instancias de esa clase otras clases que estén en su mismo paquete.
Por tanto, el package sheep contendrá las siguientes clases:
package sheep;
public class Sheep
{
private int serialNumber;
private String location;
public Sheep( int serial, String loc )
{
serialNumber = serial;
location = loc;
}
public SheepMemento getMemento( )
{
return new SheepMemento( serialNumber, location );
}
public void setMemento( SheepMemento memento )
{
serialNumber = memento.serialNumber;
location = memento.location;
}
public void setLocation( String newLoc )
{
location = newLoc;
}
public String getLocation( )
{
return location;
}
public String toString( )
{
return "Sheep: serialNumber " + serialNumber + " location " + location;
}
}
La clase SheepMemento:
package sheep;
public class SheepMemento
{
protected int serialNumber;
protected String location;
protected SheepMemento( int serial, String loc )
{
serialNumber = serial;
location = loc;
}
}
Y finalmente, el sargento:
package sergeant;
import sheep.Sheep;
import sheep.SheepMemento;
public class Sergeant
{
public void attack( )
{
Sheep mySheep = new Sheep( 1, "here" );
SheepMemento initState = mySheep.getMemento( );
System.out.println( "initial position" );
System.out.println( mySheep );
System.out.println( "change its position" );
mySheep.setLocation( "there" );
System.out.println( "final position" );
System.out.println( mySheep );
System.out.println( "retreat!" );
mySheep.setMemento( initState );
System.out.println( mySheep );
}
public static void main( String[] args )
{
Sergeant sergeant = new Sergeant( );
sergeant.attack( );
}
}
Muhahahahahahhaha. ¡¡El Profesor Dispar lo ha conseguido de nuevo!!. Ha sido capaz de encapsular el estado interno de un objeto en otro objeto distinto, y encapsularlo tan bien que ese segundo objeto sólo puede ser utilizado por el objeto que lo creó ( quien sea capaz de repetir la frase tendrá mi eterna admiración ).
Comentarios
La verdad es que no soy de escribir comentarios o sugerencias en general, pero este artículo sí lo vale!!! Es muy didáctico, claro y divertido. Te felicito por poseer la capacidad de poder lograr todo eso junto y explicando un tema "no tan sencillo" como son los patrones de diseño.
Saludos.
Leo (Argentina)
Publicado por: Leonardo | Noviembre 3, 2005 03:37 PM
Vaya, Leo, muchas gracias por tu comentario.
Publicado por: Cesar Tardaguila | Noviembre 3, 2005 05:24 PM
estoy leyendo esta increible historia (acabo de terminar con el patrón Extension Objects)... la verdad es que es una forma muy didactica e interesante de entender el funcionamiento de los patrones de diseño...
... me encuentro con este excelente material justo ahora que tengo que realizar un proyecto de programación para la uni que utilice patrones de diseño.... mil gracias por estos articulos :D
Publicado por: gringo | Noviembre 4, 2005 03:50 AM