Patrón de diseño: estrategia

Este es un ejemplo del patrón estrategia (Strategy Pattern) en java en el que se pasa como parámetro a la estrategia el context o cualquier objeto necesario para modificarlo desde la estrategia concreta. Como normalmente los ejemplos que hay de patrones de diseño lo que hacen siempre es mostrar un print por pantalla, no se entiende bien o queda poco claro el hecho de modificar el objeto. Para la definición de dicho patrón la puedes visitar en wikipedia. Si nunca lo viste lee también la definición de wikipedia

Definición Strategy Pattern

El patrón estrategia permite mantener un conjunto de algoritmos de entre los cuales el objeto cliente puede elegir aquel que le conviene e intercambiarlo dinámicamente según sus necesidades.

Esto significa básicamente que si un objeto tiene un comportamiento, lo quites de la clase. Es útil por ejemplo cuando de una clase heredan clases que no deberían heredar todos sus métodos. Por ejemplo si tienes la clase animal y luego la clase persona y la clase perro que heredan de animal. Tendrían métodos como ver, andar, correr. Pero qué pasa con hablar? no se puede poner el método hablar en animal porque si no lo heredaría perro. La solución es usar un patrón estrategia que nos permite aislar el comportamiento que cambia entre las clases a través de la composición. Para ello definirías en la clase animal un strategy y luego extenderías a esa clase con los diferentes comportamientos. Por ejemplo una persona su comportamiento sería hablar y en el perro seria ladrar. Este tipo de ejemplos es el que te puedes encontrar por la red o incluso en libros de patrones como head first design patterns ( en mi opinión de lo mejorcito) el ejemplo de los patos.

Pero lo que yo quiero es mostrar un ejemplo donde se vea como interactúan el context, que es la clase que le va a cambiar el comportamiento, con la estrategia concreta. El context en el ejemplo anterior seria la clase animal. Para esto resaltamos la parte de definición de  implementación que dice. Presta atención a la primera , luego explico porqué

Implementación

Entre las posibilidades disponibles a la hora de definir la interfaz entre estrategia y contexto, están:

  • Pasar como parámetro la información necesaria para la estrategia implica un bajo acoplamiento y la posibilidad de envío de datos innecesarios.
  • Pasar como parámetro el contexto y dejar que la estrategia solicite la información que necesita supone un alto acoplamiento entre ellos.
  • Mantener en la estrategia una referencia al contexto (similar al anterior).
Diagrama clases strategy pattern

Diagrama clases strategy pattern

Pues lo mejor es con un ejemplo: supongamos que estamos haciendo un videojuego y que tenemos una entidad Nave. Bien esta nave tiene atributos como vida, puntos, etc. Ahora supongamos que cada x puntos la nave toma un escudo y se vuelve invulnerable o lo recoge de la pantalla. Este es es un claro ejemplo de strategy donde voy a aislar ese algoritmo de comportamiento, es decir el que controle si toma el escudo. El context, donde voy a implementar el patron estrategia estará en la nave, es decir tendra una referencia a este comportamiento.

Además como voy a cambiar desde el  patrón estrategia un atributo de la nave , el escudo, voy a tener que pasarle una referencia a la nave. Para eso cuando defino mi strategy que es la clase abstracta desde la cual heredan las estrategias contretas, le paso la nave y guardo su referencia. Esto es lo que quiere dice el parrafo en negrita anteriormente descrito y es clave.

Clase Comportamiento: la estrategia

// strategy
public abstract class Comportamiento {
    Nave nave;
    Comportamiento(Nave nave){
        this.nave = nave;
    }
    abstract void ejecutar();
}

La Clase Invulnerable

Esta es una estrategia concreta. Lo que hace es que en su contructor le pasa la nave al contructor de la super clase. Luego el método ejecutar le cambia el comportamiento a la nave pasándole este mismo, es decir invulnerable y establece su escudo a true. Por este motivo necesito la nave dentro de mi strategy.

//concrete strategy
public class Invulnerable extends Comportamiento{

    Invulnerable(Nave n){
        super(n);

    }
    @Override
    void ejecutar() {

        nave.setComportamiento(this);
        nave.setEscudos(true);

    }
    public String toString(){
        return "Comportamiento invulnerable";
    }

}

Clase Vulnerable

Esta es otra estrategia concreta. Es similar a la anterior, pero le cambia el escudo a false.

//concrete strategy
public class Vulnerable extends Comportamiento{
    Vulnerable(Nave n){
        super(n);
    }
    @Override
    void ejecutar() {

        nave.setComportamiento(this);
        nave.setEscudos(true);

    }
    public String toString(){
        return "Comportamiento vulnerable";
    }
}

Lo unico que destacar de la clase nave es que cada 100 puntos se vuelve invulnerable como se comprueba en su metodo addPuntos.

Clase Nave: el context

// context
public class Nave {

    private int vidas;
    private int puntos;
    Comportamiento comportamiento;
    private boolean escudos = false;

    Nave(){
        vidas = 3;
        puntos = 0;
        comportamiento = new Vulnerable(this);
    }
    public void addPuntos(){
        puntos +=100;
        if(puntos %100 ==0){
            comportamiento = new Invulnerable(this);
        }
    }

    public Comportamiento getComportamiento() {
        return comportamiento;
    }
    public void setComportamiento(Comportamiento comportamiento) {
        this.comportamiento = comportamiento;
    }
    public boolean isEscudos() {
        return escudos;
    }
    public void setEscudos(boolean escudos) {
        this.escudos = escudos;
    }
}

Clase main

// clase principal
public class App {
    public static void main(String[] args) {
        Nave nave = new Nave();
        // mostramos el comportamiento actual de la nave 
        System.out.println(nave.getComportamiento());
        // aqui al sumar 100 puntos se vuelve invulnerable
        nave.addPuntos(); 
        // como cada 100 puntos es invulnerable, en la clase nave cambia el comportamiento a Invulnerable
        System.out.println(nave.getComportamiento());
        //cambiamos el comportamiento a vulnerable
        nave.setComportamiento(new Vulnerable(nave));
        // mostramos el toString del comportamiento actual 
        System.out.println(nave.getComportamiento()); 

    }
}

Bueno espero que sirva de ayuda el ejemplo del patrón estrategia.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s