Validaciones

Una situación muy común al construir interfaces de usuario es el hecho de admitir solamente datos válidos.

Si bien parece que eso es restricción de la interfaz de usuario al analizarlo nos damos cuenta que son restricciones del dominio, ya que las reglas que definen cuando un dato es válido surgen de él.


Veámoslo con un ejemplo.


Tenemos una aplicación  que sirve para convertir de millas a kilometros, su interfaz se ve algo como:



Y su diseño:
Y lo que queremos asegurar es que: No convierta números negativos. Este requerimiento es una nueva regla de negocio.

¿Dónde implementamos la validación?

La primer inquietud que nos surge es ¿Dónde ponemos la validación y por qué?

Podemos enunciar varias posibilidades


  • En la vista (ConversorWindow)

    • La idea seria que al modelo solo le lleguen datos validos, entonces la vista parece un gran lugar para poner esa regla

  • En el modelo (Conversor)

    • La imposibilidad de convertir es una regla propia de mi dominio, entonces en el modelo es un gran lugar

  • En el controler (Un action propio)

    • Si no nos definimos y pensamos que es algo que esta en el medio el controler ocupa justo ese lugar.

    • Pero esto va contra nuestra idea de que los controlers sean lo mas atomico posible y si les agragamos comportamiento (en especial para validar estas reglas) empieza a parecer mas un objeto de dominio que un control. Por lo que vamos a descartar esta propuesta.


De las dos opciones validas ¿por cual vamos?


Primero pensemos ...


¿Qué es una validacion?

Para nosotros es una regla que tenemos que cumplir. De esto podemos desprender dos cosas:


  • La Regla en si: algo que tenemos que cumplir

  • Ejecución de esa regla: hacer efectivo su cumplimiento.

No confundamos esas cosas, en nuestro caso la imposibilidad de convertir numeros negativos es independiente de la vista, y es algo bien intrínseco a nuestro problema. Nosotros NUNCA vamos a convertir númeross negativos, y eso es logica de mi negocio, por lo cual queremos ponerla en el modelo. Otra de las ventajas al ponerlo en el dominio ganamos facilidad de testeo, ya que la interfaz no es simple de testear y muchas veces se deja de forma no automatica.

Es por eso que vamos a elegir el modelo como lugar para enunciar la regla.


Entonces creamos un nuevo método en el Conversor, le ponemos de nombre validar y su código se nos ocurrió que sea algo como:


   public void validar(){

   if(this.getMillas() < 0){

   throw new NoSePuedeConvertirNumerosNegativosException();

   }

   }


Ahora para implementar esto vimos que queríamos que algo que pasa en el dominio se muestre en la vista, entonces de alguna manera tenemos que ver la capacidad que tiene el framework que usamos (en este caso el Arena) para mostrar errores.


Entonces, una de las cosas que se nos ocurrió es que al apretar el botón  primero se llame al validar y después se haga la conversión en sí.










Para lo que es validaciones el “Arena” nos da algunas herramientas:


  • ErrorPanel, es un componente que nos permite mostrar los mensajes de error que se producen.
  • Los bindings realizan validaciones. Por ejemplo si tenemos un textbox bindeado contra una propiedad numérica del modelo automáticamente convierte el string del textbox de la vista en un numero y si no puede realizar la conversión tira el error.
  • En cualquier momento podemos lanzar una excepción que tenga el tipo UserException y lo que va a pasar es:
    • Si tenemos un panel de errores va a mostrar el mensaje de error
    • Si tenemos bindeado algun boton contra el éxito/fracaso de las validaciones va a desactivarse
    • Si acabamos de hacer click en un boton aparece un nuevo dialogo indicando el mensaje de error
  • Existe un componente llamado TextFilter que es una interfaz, nos pide que definamos un método llamado accept que recibe un evento y devuelve un booleano (true si debe de aceptar el evento y false en caso contrario). Por ejemplo, ya existe un TextFilter que acepta solo numeros y no admite que ingresemos otro valor (TextFilter.NUMERIC_TEXT_FILTER).


Ahora, si nos ponemos el sombrero de usuario del conversor y hablamos modestamente de su usabilidad y lo que nos gustaría:


  • Que si ingresamos un valor inválido que nos informe mientras tipeamos (sin necesidad de apretar el boton)

  • Que nunca deje setear un valor en el conversor si es negativo.

  • Que no solo nos informe que hubo error sino que deshabilite el boton.

  • Que ni siquiera me deje escribir cosas inválidas.


Aca vimos hay muchas cosas que dependen del framework que estamos usando, tomamos el ejemplo de habilitar/deshabilitar el boton y lo facil que era, y esto pasaba porque:


  • elegimos validar en el domnio

  • elegimos manejar los errores como le gustan al arena (tirando la excepcion que le gusta)

Para poder cumplir con la ultima motita podemos usar un Filter, particularmente un TextFilter, vimos su codigo, un solo metodo:

   public boolean accept(TextInputEvent event);

Y lo usamos en nuestro conversor y de paso con una inner class:


millas.withFilter(new TextFilter(){

public boolean accept(TextInputEvent event){

    return StringUtils.isNumeric(event.getPotenctialTextResult());

}

});


Despues un poco nos fuimos por las ramas y dijimos, esta regla es simple, la hicimos en una linea, y se nos complico la regla: que admita comas, pensamos en mas cosas que nos pueden pasar y nuestra regla era terrible y no era una linea y tenia sentido usarla en otros lados. En ese momento vimos lo feo que era que solo existiese en esa clase y que en ese momento tener una inner class no estaba bueno, aunque sea lo sacamos a una clase nueva.


Despues nos fuimos todavia mas por las ramas y hablamos de la importancia de contribuir en proyectos de software y no estar solo en la posicion de usuarios comodos y quejarnos de que no se ajusta a nuestras necesidades, y parchear para que funcione y seguir. Tambien hablamos del tride-off y el esfuerzo que eso implica.


Volvimos y revisamos nuestro ejemplo, al final refactorizamos y dijimos:


  • Definitivamente quiero que mi regla de negocio este efectivamente en mi modelo, por lo que volvimos el metodo a la clase Conversor, en un metodo nuevo.

  • Tambien quiero que se ejecute mientras tipea el usuario se valide, entonces queremos el TextFilter y lo que vamos a hacer es que en su implementacion mande el mensaje nuevo de validacion al modelo.

Quisimos categorizar el TextFilter y dijimos dentro del MVC que es? Llegamos a la conclusion que es un controller: porque se comunica con el dominio (le manda un mensaje al modelo) actua sobre la vista (en la pantalla no aparece ese caracter) y justamente esa es la accion del controller.


Conclusion:

separemos las reglas de negocio de su ejecucion.


Comments