martes, 23 de diciembre de 2014

Arquitecura de Android SOLID(A) I

Continua en la Parte II y Parte III

Como ya comenté en una diatriba en un post anterior, la segregación de responsabilidades en Android es un maldito infierno debido a que las infames Actividades establecen los eventos de la vista, reciben los mensajes del bucle de mensajes del contexto de la aplicación, controlan su ciclo de vida, etc.

Para poder cumplir el principio de responsabilidad única y un diseño SOLID hemos de segregar las responsabilidades y proporcionar los mecanismos adecuados para su intercomunicación.

Para ello he preparado un pequeño ejemplo que, aunque muy muy básico para usarse en el mundo real, puede representar un buen comienzo y/o un buen ejemplo para que se comprenda y se pueda empezar a crear una arquitectura en Android decente.

Este ejemplo se compondrá de:
  • Un Modelo que representa el estado y el comportamiento de nuestra entidad de negocio.
  • Un Modelo de Vista que representará el estado de la vista.
  • Un Controlador que recibe las llamadas de la vista, realiza las operaciones con las entidades de dominio y manda notificaciones a la vista.
  • Una vista que muestra el modelo y el modelo de vista, llama al controlador para aplicar reglas de dominio y ofrece el feedback al usuario con las notificaciones del controlador.



En este ejemplo voy a omitir, de forma premeditada, características necesarias de Android para obtener una UI no bloqueante, como la clase Runnable, AsyncTask, y funciones como runOnUIThread para evitar meter ruido.

También omitiré toda la programación necesaria que gestiona el ciclo de vida de las actividades por la misma razón.

De esta manera me puedo enfocar en los aspectos de la arquitectura y no de la implementación, esto ayuda a leer y entender el código, y puede ser más fácilmente comprendido y aplicado en otras tecnologías.

El ejemplo que voy a poner es un sencillo marcador de partidos de tenis. En él existen 2 botones para añadir un punto al jugador uno o al jugador dos. Cuando uno de ellos hace el cuarto punto (15, 30, 40, Juego) se resetean los contadores de puntos y le se incrementa el contador de juego.

Sobra decir que el tiempo y trabajo necesario para montar una buena arquitectura desacoplada sólo merece la pena cuando se desea crear una aplicación grande y compleja. En esos casos, el retorno de la inversión es muy elevado, puesto que no sólo facilita la legibilidad, el mantenimiento y la expansión de la aplicación, si no que además, la división de responsabilidades realizada se convierte en un modelo conceptual mental que ayudará a que todo el equipo de trabajo mantenga un diseño coherente al sentirse obligado a mantener la arquitectura actual.

 

El Modelo

El modelo debe representar la información, las restricciones e invariantes y las acciones a nivel de lógica de entidad de negocio. Debido a que encapsula comportamiento que puede implicar la modificación implícita de la información que representa; el modelo debe exponer una forma de notificar esos cambios para poder actualizar la vista. Para ello utilizamos el patrón observador. Se pueden utilizar las clases observable/observer de Java o bien escribir unas propias.

public interface OnChangeListener<T> {
void onChange(T model);
}

public interface IObservable<T> {
void addListener(OnChangeListener<T> listener);
void removeListener(OnChangeListener<T> listener);
}

public class Observable<T> implements IObservable<T> {
private final ArrayList<OnChangeListener<T>> listeners = new ArrayList<OnChangeListener<T>>();
public void addListener(OnChangeListener<T> listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
public void removeListener(OnChangeListener<T> listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
protected void notifyObservers(final T model) {
synchronized (listeners) {
for (OnChangeListener<T> listener : listeners) {
listener.onChange(model);
}
}
}
}

En este caso siempre se modifica algún dato del modelo que se mostrará en la vista cuando se le pida que ejecute una acción, y parece innecesario notificar que algún dato ha sido modificado; pero existen muchos casos en la vida real en los que el modelo no cambia ningún dato (o dato visible) según su estado y lógica interna, y por ende en esos casos se omitiría la notificación de cambios para ahorrarnos el proceso de enlace con la vista.

public class MarcadorModel extends Observable<MarcadorModel > {
  public String nombreJugador1 = "Rafa Nadal";
  public String nombreJugador2 = "Roger Federer"; 

private int puntosJugador1 = 0;
private int puntosJugador2 = 0;
private int juegosJugador1 = 0;
private int juegosJugador2 = 0;

public int getPuntosJugador1() {
return puntosJugador1;
}
public int getPuntosJugador2() {
return puntosJugador2;
}

       public int getJuegosJugador1() {
return juegosJugador1;
}
       public int getJuegosJugador2() {
return juegosJugador2;
}

      public void jugador1Marca(){
puntosJugador1++;
                 if (puntosJugador1 = 4){
                     jugador1GanaJuego();
                }
                notifyObservers(this); 
}

        public void jugador2Marca(){
puntosJugador2++;
                 if (puntosJugador2 = 4){
                     jugador2GanaJuego();
                }
                notifyObservers(this); 

        private void jugador1GanaJuego();{
resetPuntosJugadores();
                juegosJugador1++;
                }
         
        private void jugador2GanaJuego();{
resetPuntosJugadores();
                juegosJugador2++;
                }

private void resetPuntosJugadores(){
                puntosJugador1 = 0;
        puntosJugador2 = 0;
        }
}

No hay comentarios:

Publicar un comentario