lunes, 5 de enero de 2015

Arquitecura de Android SOLID(A) II

El Modelo de Vista

El Modelo de Vista representa el estado de los componentes de la vista. Contiene variables que representan el estado de los controles como su visibilidad, si están activos, chequeados, si debe tener un color de fondo u otro, etc.

La lógica que encapsula el MV es la toma decisiones con respecto a la interfaz de usuario dependiendo de el Modelo y/o el estado de otros controles.

Por ejemplo, el MV podría contener el código y los estados que controlan si una lista desplegable se debe activar o desactivar dependiendo de si el usuario ha activado un checkbox y además cambia de color el fondo de una caja de texto para que al usuario no se olvide de rellenarla. Ofreciendo una función al controlador que tendría esta pinta :


checkBoxListaDesplegableActiva(activa boolean){
         this.listaDesplegableEnabled = activa // el valor del checkBox se traduce en el estado activo o desactivado de la lista desplegable
         this.editTextColor = Color.RED //cambios implícitos en otros controles
         notifyObservers(this);
}

El MV notifica que se ha cambiado el estado de algún control dependiente de forma implícita. La vista controlaría esta notificación y actualizaría el enlace de los controles con la información del MV. 

listaDesplegable.setEnabled(MV.listaDesplegableEnabled );
miEditText.getBackground().setColorFilter(MV.edtiTextColor)

El MV también se encargará de observar el Modelo en el caso de que cambios en el estado de la entidad cambien el comportamiento o el estado de controles de la vista.

En el ejemplo que nos ocupa vamos a hacer un MV que ponga el color de fondo de la etiqueta con el nombre del jugador que está ganando en verde y el del que está perdiendo en rojo.

Esta clase tiene que notificar cambios por lo que hace uso de la clase Observable que creamos en el post anterior. También recibe notificaciones del Modelo por lo que debe implementar la interfaz OnChangeListener<T>

public class MarcadorViewModel extends Observable<MarcadorViewModel> implements OnChangeListener<MarcadorModel > {

private static Color colorGanador = Color.GREEN;
private static Color colorPerdedor = Color.RED;
private static Color colorEmpate = Color.WHITE;

private enum EstadosMarcador{
EMPATE, GANAJUGADOR1, GANAJUGADOR2
}

        private EstadosMarcador estado=EMPATE;

        private Color colorJugador1 = colorEmpate;
        private Color colorJugador2 = colorEmpate;

public Color getColorJugador1() {
return colorJugador1;
}
public int getColorJugador2() {
return colorJugador2;
}

    protected void setColoresEmpate(){
        if (estado != EMPATE){
            colorJugador1 = colorEmpate;
            colorJugador2 = colorEmpate;
            estado = EMPATE;
            notifyObservers(this);
        }
    }

    protected void setColoresJugador1Gana(){
        if (estado != GANAJUGADOR1){
           colorJugador1 = colorGanador ;
           colorJugador2 = colorPerdedor ;
           estado = GANAJUGADOR1;
           notifyObservers(this);
        }
    }

    protected void setColoresJugador2Gana(){
         if (estado != GANAJUGADOR2){
            colorJugador1 = colorPerdedor;
            colorJugador2 = colorGanador;
            estado = GANAJUGADOR2;
            notifyObservers(this);
        }
    }

@Override
public void onChange(MarcadorModel  marcador) {
              if (marcador.hayEmpate()){ setColoresEmpate();  }
             elseif(marcador.jugador1Gana()){ setColoresJugador1Gana(); }
             else {setColoresJugador2Gana();}
             //Ver Nota 1
}
}

El MV recibe la notificación del modelo indicando que ha sufrido un cambio. El MV consulta el modelo para decidir que colores mostrar y actualiza su estado para detectar cambios y notificarlos.

En el modelo habría que crear las funciones hayEmpate() y jugador1Gana() que no tienen misterio ninguno y me las voy a saltar.

Con el MV conseguimos crear un contexto con estado y lógica. Mantenemos la responsabilidad única que debe tener una clase, un bajo acoplamiento con respecto a la vista y una legibilidad que aporta mayor facilidad de mantenimiento que si tuviéramos todo este código a cascoporro en la vista realizando operaciones y consultas directamente con los estados de los controles. Cumplimos (casi, aunque esto es un esqueleto y se debe refinar) la máxima del diseño orientado a objetos Pide, no preguntes. Una cosa muy importante que ganamos con esto es la capacidad de utilizar pruebas unitarias en las vistas (o lo más cercano posible); gracias a esto, un cambio en la IU que sea relativa a la lógica o a algún estado del MV, se puede probar a fondo automátiamente después de realizar el cambio.

Nota 1: Esto no se debería hacer así; se ha encapsulado correctamente la lógica que calcula si hay empate o quien gana y se podría cambiar la implementación interna sin afectar a los consumidores; pero esto sigue siendo preguntar, no pedir. La forma ideal seria implementar un sistema de eventos en los que el modelo solo notifica los cambios cuando ocurran. Mientras un jugador esta ganando no se lanzaría ningún evento; cuando los jugadores empatan se lanzaría el evento onJugadoresEmpatan que seria escuchado por el modelo de vista y solo en ese momento se ejecutaría el código que cambia los colores; lo mismo pasaría cuando un jugador empiece a ganar. De esta forma, mientras un jugador siga ganando, aunque el modelo ha cambia porque ha conseguido un nuevo punto, no se ejecuta ningún código innecesario en el modelo de vista como pasa ahora. Mejoramos el rendimiento de la aplicación.

No hay comentarios:

Publicar un comentario