« Un ejemplo del Patrón Prototype | Inicio | Planeta código »

Un ejemplo del Patrón Prototype ( la versión Java )

El Profesor Dispar es un científico español que está planeando dominar el mundo. ¿Quieres conocer los problemas con los que se ha encontrado, y cómo los ha resuelto implementando el patrón prototype?

El Profesor Dispar era un respetado científico español, que se convirtió en un genio del mal por sus malas condiciones laborales ( y por que su novia se la pegaba con otro, la verdad ). Ahora, su único deseo es llevar a cabo su malvado plan para ¡¡¡dominar el mundo!!!

professorCoupling.jpg
El Profesor Dispar

Así que, en sus delirios paranoides, ha construido una máquina para clonar. Una máquina para clonar muy especial, porque puede ser programada en Java

El plan del Profesor Dispar es clonar todas las ovejas que pueda, para así controlar la economía mundial y dominar el mundo ( la verdad, no entiendo el porqué de ese plan, pero recuerda, aunque esté loco, es un genio! ).

sheep.jpg
Una oveja ( vista frontal )

La máquina para clonar es algo así:

class CloningMachine { public CloningMachine( ) { } public Sheep buildClone( ) { return new Sheep( ); } public Sheep[] buildManyClones( int cloneNum ) { Sheep[] returnArray = new Sheep[ cloneNum ]; for( int k=0; k< clonesNum; k++ ) { returnArray[ k ] = new Sheep( ); } return returnArray; } }

Y la oveja:

class Sheep { public Sheep( ) { System.out.println( "beeeee. Soy una oveja recién creada" ); } }

Tras inundar el mundo con ovejas clonadas ( es su malvado plan, no el mío ), el Profesor Dispar se dio cuenta que clonar unos cuantos miles de vacas le ayudaría a dominar el mundo más rápido. Pero su máquina para clonar no fue construida para clonar vacas, sino para clonar ovejas

cow.jpg
Una vaca ( vista lateral )

Así que el malvado genio del mal decide añadir a su máquina la funcionalidad necesaria para clonar vacas. ¿Cómo?. Mira:

class CloningMachine { function CloningMachine( ) { } public Sheep buildClone( ) { return new Sheep( ); } public Cow buildCowClone( ) { return new Cow( ); } public Sheep[] buildManyClones( int cloneNum ) Sheep[] returnArray = new Sheep[ cloneNum ]; for( int k=0; k< clonesNum; k++ ) { returnArray[ k ] = new Sheep( ); } return returnArray; } }

El Profesor Dispar está loco, pero no es idiota. Por tanto, enseguida se da cuenta de que puede encontrarse con problemas si quiere clonar otros animales, como, por ejemplo, pájaros, o incluso humanos

Tras pensar cuidadosamente sobre su problema, hace otro intento:

class CloningMachine { function CloningMachine( ) { } public Object buildClone( String type ) { if( type.equals( "sheep" ) ) { return new Sheep( ); } else if ( type.equals( "cow" ) ) { return new Cow( ); } } public Sheep[] buildManyClones( int cloneNum ) Sheep[] returnArray = new Sheep[ cloneNum ]; for( int k=0; k< clonesNum; k++ ) { returnArray[ k ] = new Sheep( ); } return returnArray; } }

Tras varios minutos de las típicas risas histéricas, el Profesor Dispar empieza a pensar en la forma en que ha implementado su máquina para clonar, y enseguida encuentra varios puntos débiles.

En primer lugar, el método buildClone devuelve un Object, no una oveja ( Sheep ) o una vaca ( Cow ). ¿Por qué?. Porque este método no sabe lo que va a crear a priori. El Profesor Dispar ( recuerda, está loco, pero no es idiota ) barrunta que ésta no es la mejor solución posible

También se da cuenta que si en algún momento quisiera clonar algún otro animal, debería cambiar de nuevo su máquina para clonar. Y eso no hace su máquina fácil de mantener.

Pero entonces, una idea empieza a abrirse paso en su malvado cerebro. Aún no sabe exactamente cómo hacerlo, pero ¿qué pasaría si pudiera darle algo a la máquina( una oveja, una cabra, un humano, o una piedra ) y pedirle a la misma que le devolviera 500 copias del mismo?. ¡¡Brillante!! ( más risas histéricas ). Así, no tendría que preocuparse de cómo funciona la máquina, simplemente la máquina le devolvería tantas copias como necesitara del original que él le facilitara.

Entonces, el Profesor Dispar recuerda sus tiempos de estudiante, cuando leyó un libro llamado "Patrones de diseño: Elementos de software orientado a objetos reutilizable" ( el GoF ), y aquella cosa que se llamaba el "patrón prototype"

Por tanto, la máquina para clonar no puede saber qué tiene que clonar ni cómo debe clonarlo. Ésa ( la creación de la copia ), será una responsabilidad de cada uno de los animales ( o cosas a clonar ). La máquina de clonar, simplemente recibirá un animal, y le dirá a éste que produzca tantas copias de sí mismo como se necesiten, y luego se las devolverá al Profesor Dispar.

De hecho, el Profesor Dispar se ha dado cuenta de que la forma en la que deben crearse las nuevas copias de los animales no ha de ser la misma para todos, sino que en algunos casos le va a interesar utilizar el mecanismo de clonado de Java, y en otros tener un control más fino del mismo.

Así que, la primera tarea del Profesor será crear un interfaz ( que extiende Cloneable, implementando de este modo el método clone( ) ), que será el que implementen tanto la Vaca como la Oveja

public interface CloneableAnimal extends Cloneable { public CloneableAnimal duplicate( ); }

[ Nota ]. Sería posible que todos los animales extendieran de una clase base, y que por tanto el tipo que devuelve el método duplicate( ) fuera esa clase base. Pero yo voy a continuar mi ejemplo con este interfaz, porque de esta forma el diseño es mucho más flexible.

Ese método duplicate( ) será el encargado de crear y devolver una nueva copia de cada animal que lo implemente.

La oveja ahora será:

public class Sheep implements CloneableAnimal { public Sheep( ) { System.out.println( "Creada la plantilla de la oveja" ); } public CloneableAnimal duplicate( ) { System.out.println( "La oveja se va a clonar a sí misma" ); Sheep returnValue = null; try { returnValue = ( Sheep ) super.clone( ); } catch( Exception e ) { System.out.println( "error cloning Sheep" ); } return returnValue; } public String toString( ) { return "Soy una oveja clonada, beeeeee"; } }

Como se puede ver, el método duplicate( ) de la oveja ejecuta el método clone( ) de su superclase.

Aquí está la vaca:

public class Cow implements CloneableAnimal { public Cow( ) { System.out.println( "Creada la plantilla de la vaca" ); } public CloneableAnimal duplicate( ) { System.out.println( "creando una nueva instancia de Cow" ); return new Cow( ); } public String toString( ) { return "Muuuu, soy un clon de vaca" ; } }

Ahora, el Profesor Dispar tan sólo tendrá que hacer algo así:

public class ProfessorCoupling { public static void sayIt( String words ) { System.out.println( "" ); System.out.println( words ); System.out.println( "" ); } public static void main( String[] args ) { CloningMachine cMachine = new CloningMachine( ); sayIt( "creating Sheep and Cow templates" ); Sheep sheepTemplate = new Sheep( ); Cow cowTemplate = new Cow( ); Cow clonedCow = ( Cow ) cMachine.newClone( cowTemplate ); sayIt( "primera vaca clonada" ); Sheep clonedSheep = ( Sheep ) cMachine.newClone( sheepTemplate ); sayIt( "first cloned sheep" ); System.out.println( clonedSheep ); sayIt( "Creando 10 vacas nuevas" ); CloneableAnimal[] newCows = cMachine.cloneMany( 10, cowTemplate ); sayIt( "Creando 10 ovejas nuevas" ); CloneableAnimal[] newSheeps = cMachine.cloneMany( 10, sheepTemplate ); sayIt( "Probando las vacas" ); for( int i=0; i< newCows.length; i++ ) { System.out.println( newCows[ i ] ); } sayIt( "Probando las ovejas" ); for( int i=0; i< newSheeps.length; i++ ) { System.out.println( newSheeps[ i ] ); } } }

Ahora, el Profesor Dispar puede seguir inundando el mundo con los clones creados por su máquina ( más risas histéricas ), sabiendo que puede crear copias de todo lo que le dé la gana, porque le ha dado a su máquina el "don" de crear objetos sin saber de qué tipo deben ser.

Pero el Profesor es un hombre de ciencia, y en su afán por alcanzar el conocimiento absoluto, sigue leyendo documentación sobre el patrón prototype, y entonces se da cuenta de que también ha separado el código que crea los objetos del código que maneja los detalles de la creación de nuevos objetos ( eso queire decir que, por ejemplo, si quiere crear 1000 ovejas rojas hoy, y 1000 ovejas azules mañana, tan sólo debe añadir una máquina para pintar animales al lado de la máquina para clonar, y esa máquina de pintar se encargará de darlas color, sin saber cómo ni donde se han creado esos animales ).

Gracias a Celia Carracedo, hoy concretamente por los dibujos de la oveja, la vaca, y el malvado Profesor Dispar.

Comentarios

Cuando en la clase ProfessorCoupling haces:
CloningMachine cMachine = new CloningMachine( );

no indicas a que clase de las 2 que has llamado CloningMachine, aunque me parece q te refieres a una tercera de la cual no has puesto el código

Efectivamente.

Tremenda cagada. La clase CloningMachine correcta es:

public class CloningMachine
{
public CloningMachine( )
{

}

public Clonable createCopy( IClonable template )
{
return template.getCopy( );
}

public Clonable[] createManyCopies( int numCopies, IClonable template )
{
Clonable[] returnArray = new Clonable[ numCopies ];
for( int i=0; i< numCopies; i++ )
{
returnArray[ i ] = template.getCopy( );
}

return returnArray;
}
}


Gracias por darte cuenta del error!

Un ejemplo cojonuti!!!
muy bueno de verdad, divertido y eficaz.