Tiempo de lectura: 5 minutos

Patrón Estado o State, deshazte de condiciones interminables

En mi última entrevista de trabajo, el responsable del departamento técnico de Android me preguntó sobre mis patrones favoritos. Tras responderle, él me comentó que el suyo era el State/Estado. Aunque enseguida lo identifiqué debo reconocer que no estaba entre mis patrones habituales.

En cuanto se acabó la entrevista, lo primero que hice fue coger el libro Patrones de diseño y lo busqué para saber por qué era uno de sus patrones de diseño más usado. Tras leerlo minuciosamente me quedé sorprendido porque identifiqué muchos problemas a los que me había enfrentado y en los cuales podría haber aplicado perfectamente este patrón para su resolución.

¿Qué había pasado para que no lo hubiera usado antes? ¿No lo entendí en su momento? ¿No lo leí con atención? Creo que cuando te pones a estudiar un patrón sin haberte enfrentado antes a un problema en el cual se puede aplicar es más difícil que lo tengas identificado. También se puede dar el caso que se tenga “patronitis” y se empiece a aplicar patrones porque sí, creo que por ahí pasamos todos, pero yo ya pasé esa etapa y ahora prefiero no aplicar ninguno a aplicarlo mal. Tras esto, volveré a empezar a leerme todos los patrones del libro de nuevo 😉

Patrón State | Estado

El patrón State es un patrón de comportamiento que permite realizar acciones diferentes dependiendo del estado en el que se encuentre. Normalmente este estado es un atributo del objeto que cambia de valor.

El patrón State nos puede ser de gran utilidad en aquellos casos donde una entidad tiene un atributo y de su valor depende el realizar múltiples acciones. Llevado a la práctica, si estáis programando una entidad que tiene asociada un grafo de estados con múltiples transiciones y tenéis múltiples if..else o un switch que puede ir creciendo, parad, pensar y mirad si las acciones dependen de un estado del objeto, si es así, puede ser que este patrón encaje en vuestro problema.

Show me the code

Solicitan una aplicación para realizar una acción según el tiempo que haga en cada momento. El tiempo se toma cada 5 minutos y puede darse las siguientes opciones: Sol, lluvia, nieva, nublado. En el momento del desarrollo, sólo se contempla estas cuatro opciones pero debe dejarse lo más abierto posible pues se podrían añadir nuevos estados de tiempo.

TypeWeather.java

En nuestro código tendremos cuatro clases que implementarán la interfaz TypeWeather. Esta interfaz define los tipos de estado que puede tener el tiempo. TypeWeather tendrá un método que deberán implementar todos los tipos ‘executeAction’. Este método realizará una acción según el tipo de estado del tiempo.

public abstract class TypeWeather {

    public abstract void executeAction();

}

Sunny.java

En la clase Sunny, el método implementado de la interfaz enviará un email:

public class Sunny extends TypeWeather {

    String addrEmail = "demo@demomail.com";

    @Override
    public void executeAction() {
        Log.d("@PatternState","Mi objetivo es enviar un email:" + addrEmail);
    }
}

Cloudy.java

En la clase Cloudy, el método implementado realizará una petición al web service:

public class Cloudy extends TypeWeather {

    private String requestApi = "/api/demoWS";

    @Override
    public void executeAction() {
        Log.d("@PatternState", "Mi objetivo es hacer una petición web service:" + requestApi );
    }
}

Raining.java

En la clase Raining, el método implementado enviará una señal digital a un dispositivo externo para cerrar la ventana:

public class Raining extends TypeWeather{

    int digitalSignal = 0;

    @Override
    public void executeAction() {
        Log.d("@PatternState", "Mi objetivo es enviar una señal digital para cerrar las ventanas:" + digitalSignal);
    }
}

Snowing.java

Por último, la clase Snowing mostrará un gif animado con copos de nieve cayendo.

public class Snowing extends TypeWeather {

    private String nameGif;

    @Override
    public void executeAction() {
        Log.d("@PatternState", "Mi objetivo es mostrar una imagen con un gif de nieve:" + nameGif);
    }
}

Weather.java

Ahora vamos a definir la clase que contiene el atributo typeWeather y que permite realizar una acción u otra según cambie su estado. Para llevar a cabo el cambio, tendremos el método request(). También se podría haber omitido y hacer realizado justo el cambio en el setTypeWeather pero personalmente, creo que así queda más limpio porque puede ser que en algún momento asignemos el tipo y no queramos ejecutar nada.

public class Weather {

    private Date date;
    private String province;    
    private TypeWeather typeWeather;

    public void setTypeWeather(TypeWeather typeWeather) {
        this.typeWeather = typeWeather;
    }

    public void request(){
        if (typeWeather != null)
            typeWeather.executeAction();
    }
}

MainStatePatter.java

Esta clase es donde voy a gestionar el cambio de estados del ejemplo. Es una clase muy trivial, se tiene que pensar que el cambio del estado se puede producir en cualquier momento de la aplicación: al pulsar un botón, al recibir un servicio web, etc.

public class MainStatePattern {

    public  void init(){
        Weather weather = new Weather();

        //cambio de estado a NIEVE: la app mostrará por pantalla un gif animado.
        weather.setTypeWeather(new Snowing());
        
        //cambio de estado a LLUVIA: se envia señal digital
        weather.setTypeWeather(new Raining());
        
        //también se podría recibir desde un método
        weather.setTypeWeather(getCurrentTypeWeather());
    }
    
    private TypeWeather getCurrentTypeWeather(){
        //Ejemplo trivial, se podría obtener de una API, sensor, etc.
        return new Sunny();
    }
}

Tenéis todo el código subido en github. El ejemplo es muy trivial y la finalidad es que se entienda, seguramente en el día a día puede ser más complicado de detectar.