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 guardar· y despuÈs se la pasar· a su sargento.
import sheep.Sheep
class sheep.SheepMemento
{
private var serialNumber: Number;
private var location: String;
private var creator: Sheep;
function SheepMemento( serialNumber: Number, location: String )
{
this.serialNumber = id;
this.location = location;
}
public function setLocation( newLocation: String )
{
this.location = newLocation;
}
public function getState( creatorRef: Sheep )
{
return { serialNumber: this.serialNumber, location: this.location };
}
La clase Sheep
import sheep.SheepMemento
class sheep.Sheep
{
var serialNumber: Number;
var location: String;
function Sheep( serial: Number, loc: String )
{
this.serialNumber = serial;
this.location = loc;
}
public function getMemento( ): SheepMemento
{
return new SheepMemento( this.serialNumber, this.location );
}
public function setMemento( state: SheepMemento )
{
var stateData: Object = state.getState( );
this.serialNumber = stateData.serialNumber;
this.location = stateData.location;
}
//Public setters. Change inner state
public function setLocation( newLocation: String )
{
this.location = newLocation;
}
//For debugging purposes
public function toString( ): String
{
return "Sheep id: " + this.serialNumber + " @ " + this.location;
}
}
Y finalmente, el sargento:
import sheep.Sheep
import sheep.SheepMemento
class sergeant.Sergeant
{
function Sergeant( )
{
trace( "grrrrr, I'm the Sergeant" );
}
public function attack( )
{
var sheep: Sheep = new Sheep( 1, "here!" );
trace( sheep );
var sheepMemento: SheepMemento = sheep.getMemento( );
sheep.setLocation( "theeeeeere" );
trace( sheep );
sheep.setMemento( sheepMemento );
trace( sheep );
}
public static function start( )
{
var sergeant: Sergeant = new Sergeant( );
sergeant.attack( );
}
}
°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.
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.
Pero hacer lo mismo en actionscript no es tan f·cil. No existe el modificador ìprotectedî ( ni su funcionalidad ). ( se para la m˙sica heroica )
El Profesor Dispar ( la m˙sica heroica comienza de nuevo ) es un programador flash y lo ha sido durante muchos aÒos, asÌ que su capacidad para buscar soluciones a los problemas irresolubles es legendaria. Por tanto, enseguida encuentra una posible soluciÛn. øPero cÛmo?. El Profesor piensa: ìsi la oveja pasa una referencia a sÌ misma cuando crea el memento, puede chequear esa referencia otra vez cuando tiene que volver a su estado anterior, y de esa forma asegurar que ha sido ella la que creÛ el memento que se le pasaî ( muhahahahahahhahahaha ). Se lo intenta explicar a la oveja, pero ya se sabe, hay veces que si quieres hacer algo bien, lo tienes que hacer t˙ mismoÖ
Por tanto, cambia la forma en la que la oveja crea el memento, y la forma en la que vuelve a su estado anterior a partir de la informaciÛn guardada en el mismo ( que es la parte m·s importante ). Antes de devolver los datos correspondientes, se chequea si quien pide esos datos es el mismo que el que creÛ el memento. Si lo es, se devuelven datos, y si no lo es, dice ìbeeeeeî ( la cosa no da para m·s ).
Por tanto, finalmente la cosa quedar· asÌ ( lo primero de todo, la clase SheepMemento ):
import sheep.Sheep
class sheep.SheepMemento
{
private var serialNumber: Number;
private var location: String;
private var creator: Sheep;
function SheepMemento( serialNumber: Number, location: String, creator: Sheep )
{
this.serialNumber = id;
this.location = location;
this.creator = creator;
}
public function getState( creatorRef: Sheep )
{
var returnValue: Object = null;
if( this.creator == creatorRef )
{
returnValue = { serialNumber: this.serialNumber, location: this.location };
}
return returnValue;
}
}
La clase Sheep:
import sheep.SheepMemento
class sheep.Sheep
{
var serialNumber: Number;
var location: String;
function Sheep( serial: Number, loc: String )
{
this.serialNumber = serial;
this.location = loc;
}
public function getMemento( ): SheepMemento
{
return new SheepMemento( this.serialNumber, this.location, this );
}
public function setMemento( state: SheepMemento )
{
var stateData: Object = state.getState( this );
if( stateData != null )
{
this.serialNumber = stateData.serialNumber;
this.location = stateData.location;
}
else
{
trace( "memento not valid" );
}
}
//Public setters. Change inner state
public function setLocation( newLocation: String )
{
this.location = newLocation;
}
//For debugging purposes
public function toString( ): String
{
return "Sheep id: " + this.serialNumber + " @ " + this.location;
}
}
Y el sargento:
import sheep.Sheep
import sheep.SheepMemento
class sergeant.Sergeant
{
function Sergeant( )
{
trace( "grrrrr, I'm the Sergeant" );
}
public function attack( )
{
var sheep: Sheep = new Sheep( 1, "here!" );
trace( sheep );
var sheepMemento: SheepMemento = sheep.getMemento( );
sheep.setLocation( "theeeeeere" );
trace( sheep );
sheep.setMemento( sheepMemento );
trace( sheep );
}
public static function start( )
{
var 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 ).
SÛlo una cosa m·s. øQuÈ te parece la forma en la que el Profesor ha implementado el chequeo de la identidad de la oveja?. øTe gusta la soluciÛn?.
En principio podrÌa resolverse tambiÈn de la siguiente forma: en vez de guardar en el memento una referencia al creador del mismo, se puede guardar el id del creador, o un hashcode de ese objeto, o algo similar. øAlguna idea?