Cómo maximizar la reutilización de la interfaz de usuario de Android: 5 errores comunes

Durante los últimos meses, tuve la oportunidad de volver a visitar algunas de nuestras interfaces de usuario existentes en Groupon. Como parte de este proceso, comenzamos identificando qué es bueno en nuestra interfaz de usuario y cuáles son nuestros errores más comunes, con la esperanza de llevar la interfaz de usuario de Groupon al siguiente nivel.

¿Qué hace que la interfaz de usuario sea buena? Lo bueno es relativo, pero fue fácil acordar tres principios principales que se pueden aplicar a cualquier software:

  • Legible. Los buenos componentes de la interfaz de usuario son fáciles de leer. No es necesario visitar la pestaña de diseño de Android Studio, el xml se explica por sí mismo.
  • Probable Cada componente de la interfaz de usuario se puede probar fácilmente sin componentes externos o dependientes.
  • Reutilizable Los componentes de la interfaz de usuario pueden reutilizarse y ampliarse sin cambiar sus comportamientos originales.

En este artículo, veremos algunos errores comunes que afectan la reutilización de nuestra IU y veremos cómo las IU reutilizables pueden hacer que nuestro código sea más legible y comprobable.

1. Modelo de negocio en vistas personalizadas

A veces, para ahorrar tiempo y evitar el código duplicado, decidimos reutilizar en nuestras vistas, los mismos modelos que usamos en nuestra red y modelos de bases de datos. Tomemos un ejemplo:

Usamos userApiClient para extraer la información de nuestro usuario del backend:

En caso de éxito, recibimos el modelo:

Y pasamos ese mismo modelo a nuestra vista personalizada UserView, que mostrará el nombre y la dirección del usuario. Esto puede parecer una victoria fácil, pero no lo es. Va en contra de nuestro principio de reutilización y puede causar serios problemas a largo plazo. Veamos por qué.

¿Por qué es esto malo?

  • La razón más importante por la que no deberíamos reutilizar nuestros modelos de back-end es que no podremos reutilizar nuestra vista en una parte diferente de la aplicación. Estamos acoplando nuestra interfaz de usuario con una respuesta de punto final y ahora necesitamos ese punto final para representar nuestra interfaz de usuario.
  • El modelo de fondo probablemente tenga más información de la que necesita nuestra vista. Información adicional significa, complejidad adicional. Cuanta más información contenga el modelo, más fácil será usar un campo incorrecto. En el ejemplo anterior, podríamos haber usado un modelo simplificado:
  • ¿Qué sucede si el backend cambia? Tendemos a suponer que los servicios de back-end que respaldan nuestra aplicación nunca cambiarán. Esto definitivamente no es cierto, nuestro backend cambiará y necesitamos estar preparados para ello. No deberíamos cambiar nuestra interfaz de usuario solo porque algo cambió en el backend.

¿Como arreglarlo?

Solucionar este problema es fácil pero requiere un código adicional. Necesitaremos crear un modelo de interfaz de usuario y un método para transformar el modelo de fondo en el modelo de interfaz de usuario.

En nuestro ejemplo, convertiremos UserModel en UserUIModel y lo pasaremos como parámetro a UserView:

Nuestra nueva implementación requiere un poco de fontanería adicional y agrega la complejidad de una nueva transformación, pero este es un pequeño precio a pagar para desacoplar nuestra interfaz de usuario de nuestro backend. Nuestro nuevo diseño nos impide enviar datos innecesarios a nuestras vistas y nos permite reutilizar nuestras vistas donde las necesitemos.

2. Vistas monolíticas

¿Alguna vez ha abierto un archivo de diseño y ve un documento enorme que contiene toda la IU de la actividad? Este es, desafortunadamente, un patrón muy común en Android que nos causa muchos problemas.

¿Por qué es esto malo?

Va en contra de nuestros tres principios:

  • Estos archivos son realmente difíciles de leer, hay demasiada información y demasiadas capas de componentes que viven juntas.
  • No es posible probar cada parte del xml individualmente. Tendemos a probar solo nuestra interfaz de usuario como parte de las pruebas de integración ejecutadas en espresso. Las pruebas de integración son excelentes, pero combinan la interfaz de usuario y la lógica empresarial, lo que dificulta saber cuál es la causa subyacente de un error. Al dividir nuestros xmls monolíticos en pequeñas vistas personalizadas y eliminar toda la lógica empresarial de nuestra interfaz de usuario, habilitamos las pruebas de interfaz de usuario solamente. Estas pruebas pueden detectar problemas en nuestra interfaz de usuario a un nivel de granularidad más fino que las pruebas de integración regulares.
  • No podemos reutilizar partes individuales de nuestro xml, esto nos obliga a copiar y pegar esos componentes individuales para reutilizarlos en una pantalla diferente. Con el tiempo, los componentes duplicados comenzarán a divergir, causando inconsistencia en nuestra aplicación.

¿Como arreglarlo?

Construir toda nuestra lógica de UI en un archivo xml es equivalente a construir toda nuestra lógica de negocios en la clase de actividad. Deberíamos dividir nuestra interfaz de usuario en partes más pequeñas, de la misma manera, que construimos DAO, modelos y clientes de back-end para nuestra lógica de negocios.

Las vistas personalizadas y las etiquetas y son nuestros mejores amigos. Siempre debemos usarlos para romper nuestra interfaz de usuario al comienzo de cualquier nueva característica. Esperar a que se reutilice un componente de la interfaz de usuario para separarlo de un xml monolítico puede causar problemas graves. En ese momento, nuestra interfaz de usuario se asociará en gran medida con nuestra actividad / fragmento, lo que hará que el refactorizador sea más difícil y ponga en peligro las funcionalidades existentes de nuestra aplicación.

Veamos un ejemplo inspirado en un diseño real de un proyecto de código abierto. Eliminé propiedades y agregué comentarios para que el diseño sea más fácil de leer:

Incluso después de eliminar propiedades y agregar comentarios, leer un xml como este es difícil. Hay varias capas de diseños anidados, lo que dificulta la comprensión de dónde se coloca cada componente. Sin los comentarios, es difícil saber cómo se relacionan las diferentes etiquetas y qué representan.

En el xml podemos identificar al menos 6 componentes de IU bien definidos. Veamos cómo se verá este diseño si creamos una vista personalizada para estos componentes:

Al crear una vista personalizada para cada una de nuestras vistas, simplificamos nuestro código, hicimos nuestras vistas reutilizables y evitamos los efectos secundarios de futuros refactores. En esta nueva implementación, los comentarios ya no son necesarios porque nuestro código es autodescriptivo.

Piense lo fácil que será implementar una nueva animación de progreso si todas nuestras actividades usan ProgressOverlay. Solo será necesario cambiar una clase frente a todas nuestras actividades utilizando el enfoque monolítico.

El enfoque de Groupon de favorecer pequeños componentes de la interfaz de usuario sobre xmls monolíticos está inspirado en el libro Atomic Design de Brad Frost. Recomiendo echar un vistazo a este libro, especialmente si te apasiona la interfaz de usuario.

3. Lógica empresarial en vistas personalizadas

En el punto anterior, hablamos sobre los beneficios de usar vistas personalizadas, las vistas personalizadas son una herramienta increíble, pero si no las usamos con cuidado, pueden ser un arma de doble filo. Acoplando nuestra lógica empresarial a una vista personalizada, es sorprendentemente fácil. Agregar registros, pruebas AB y lógica de toma de decisiones puede parecer una excelente manera de compartimentar nuestro código, pero no lo es.

¿Por qué es esto malo?

  • La lógica en nuestros puntos de vista hace que sea más difícil para nosotros reutilizarlos ya que la lógica está acoplando nuestro punto de vista con nuestro caso de uso actual. Para que nuestros puntos de vista sean totalmente reutilizables, deben seguir siendo simples y agnósticos a la lógica empresarial. Una vista bien implementada solo debe recibir un estado y representarlo sin tomar ningún tipo de decisión.
  • Agregar lógica empresarial a nuestras opiniones hace que sea más difícil probar nuestras opiniones. Las vistas bien implementadas solo dependen de componentes de IU más pequeños. Se pueden probar fácilmente de forma aislada como parte de una prueba de automatización. Una buena cobertura de automatización, a nivel de vista, nos ayudará a detectar los efectos secundarios no deseados anteriores de los refactores y los cambios de código en nuestra interfaz de usuario.

¿Como arreglarlo?

Hay muchas maneras de extraer la lógica empresarial de nuestros puntos de vista. La forma de hacerlo dependerá de su arquitectura preferida. Si usa MVP, cualquier tipo de lógica pertenece al presentador. Si prefiere MVVM, su lógica debería ser parte de su ViewModel.

En Groupon, hemos descubierto que los flujos de datos MVI y unidireccionales son una excelente manera de solucionar este problema. La lógica de negocios debe ser parte de nuestra intención, que producirá un objeto de estado inmutable, que será representado por nuestra opinión.

Si está interesado en los flujos de datos unidireccionales y cómo implementar componentes de IU reutilizables. Le recomiendo encarecidamente que lea el artículo de Pratyush Kshirsagar y Yichen WU Comunicación cruzada utilizando Grox. Hacen un gran trabajo al explicar cómo los flujos de datos unidireccionales pueden ayudarnos a construir nuestra interfaz de usuario.

4. Sobre-optimización

En este punto, es posible que se haya dado cuenta de que no hemos hablado sobre el rendimiento. Incluso podría sorprenderse de que ni siquiera consideráramos el rendimiento como uno de nuestros principios para una buena interfaz de usuario.

Si bien creemos que el rendimiento es importante, el código legible, reutilizable y comprobable es aún más importante que el código optimizado. Después de todo, esa es la razón por la que usamos lenguajes de programación de alto nivel en lugar de escribir código de ensamblador más eficiente.

En Android, los diseños anidados y la doble imposición son dos grandes problemas que afectan el rendimiento de nuestra interfaz de usuario. Debido a eso, estamos constantemente bombardeados con artículos, podcasts y charlas que nos dicen que usemos ConstrainLayout y evitemos diseños anidados. ConstrainLayout es una herramienta increíble, es más potente que RelativeLayout y no incurre en doble imposición. El problema, como siempre, sucede cuando llevamos esto al extremo.

Con base en todos los artículos y charlas que hemos escuchado, podemos decidir que implementaremos la interfaz de usuario de nuestra actividad solo con un diseño de restricción.

¿Por qué es esto malo?

  • Al construir toda nuestra interfaz de usuario en un diseño de restricción, estamos creando una interfaz de usuario monolítica. Lo cual, como discutimos anteriormente, es difícil de leer, difícil de probar y produce código no reutilizable.
  • No estamos tratando nuestra IU como un ciudadano de primera clase. Nunca consideraremos construir toda nuestra lógica de negocios en la clase de actividad / fragmento. Exactamente las mismas razones se aplican para no construir nuestra UI un archivo xml.

¿Como arreglarlo?

Sacrificar un buen código para el rendimiento es una elección difícil y siempre debe ser nuestro último recurso. Para evitar una optimización excesiva, debemos hacer que las pruebas de rendimiento formen parte de nuestro proceso de desarrollo. Nuestra prueba debe indicarnos cuándo tenemos un problema y solo debemos crear una vista monolítica cuando no hay otra solución posible. Te sorprenderá cuántas veces los problemas de rendimiento de la interfaz de usuario son causados ​​por nuestra lógica de enlace que hace demasiado o porque nuestro código está actualizando la interfaz de usuario más de lo necesario.

5. Descuidar nuestra interfaz de usuario en revisiones de código

Revisar el código es difícil, requiere mucho tiempo y, para empeorarlo, los archivos xml no son fáciles de entender (especialmente los xmls monolíticos). Por esas razones, tendemos a descuidar nuestra interfaz de usuario cuando revisamos el código.

¿Por qué es esto malo?

  • La interfaz de usuario de nuestra aplicación es su carta de presentación para nuestros usuarios. Una interfaz de usuario coherente, limpia y bien estructurada puede ser la diferencia entre que nuestros usuarios se queden o abandonen nuestra aplicación.
  • No estamos tratando nuestra IU como un ciudadano de primera clase. Nuestra interfaz de usuario es la mitad de nuestra aplicación, pasar tiempo revisando xmls y vistas es, al menos, tan importante como revisar nuestra lógica de negocios.

¿Como arreglarlo?

Hay algunas cosas que podemos hacer para mejorar nuestro proceso de revisión.

Como revisor:

  • Está perfectamente bien reconocer que no podemos entender un xml. Probablemente no tenga el contexto correcto o la IU es demasiado compleja. Solicite ayuda del autor del código.
  • No se sienta mal por pedirle al autor que divida un xml en vistas más pequeñas. Hará lo mismo con la clase grande o un método grande.
  • Comience la revisión del código con la interfaz de usuario. Como dije, los archivos xml son la parte más difícil de revisar, no espere a estar cansado para comenzar a leerlos.
  • Familiarícese con las pautas de diseño de materiales. Algunas cosas fáciles que siempre verifico son: ¿Tenemos un efecto dominó como parte de nuestro estado "presionado"? ¿Tenemos / necesitamos elevación en nuestros botones? o ¿Nuestras animaciones tienen la duración correcta?

Como comentado:

  • Agregue capturas de pantalla de su IU en su solicitud de extracción. Esto ayudará a su equipo a revisar el código más rápido.
  • Pídale a su diseñador que revise su implementación. Tu diseñador es tu mejor aliado. Pídales que revisen su IU lo antes posible en el ciclo de desarrollo.
  • Evite los archivos xml monolíticos. No puedo decir lo suficiente, los pequeños componentes de la interfaz de usuario son mejores y más fáciles de revisar.
  • Comience con su interfaz de usuario. Inicie cualquier función nueva creando una solicitud de extracción dedicada a su interfaz de usuario. De esta manera, sabrá que su interfaz de usuario tendrá el 100% de la atención del revisor.

Conclusiones

Construir componentes de interfaz de usuario reutilizables no es difícil, pero requiere disciplina. Requiere que dejemos de pensar en términos de pantallas y comencemos a pensar en términos de componentes y sus relaciones.

Reusar nuestra interfaz de usuario nos ayuda a construir nuevas funcionalidades más rápido. Hay una gran diferencia, en términos de velocidad y calidad, entre componer componentes de interfaz de usuario existentes y construir una interfaz de usuario completa desde cero. Además, los componentes reutilizados contienen correcciones de errores y consideran casos extremos en los que quizás nunca hayamos pensado.

Resumiendo algunos de los consejos que hemos discutido:

  • Usar modelos de negocios para vistas es una mala idea. Desacople siempre sus puntos de vista de su backend y su lógica empresarial.
  • Evita los xmls monolíticos. Hacen que su código sea difícil de leer, difícil de probar y son la causa principal de inconsistencias en una aplicación. El diseño atómico puede ayudarlo a dividir su interfaz de usuario en componentes reutilizables.
  • La lógica empresarial no pertenece a la interfaz de usuario. El registro, las pruebas AB o la lógica de toma de decisiones no pertenecen a una Vista. Las vistas solo deben recibir un estado inmutable y representarlo. Adoptar MVI, la inmutabilidad y los flujos de datos unidireccionales lo ayudarán.
  • La optimización a expensas de la calidad del código debería ser una excepción, nunca una regla. Solo debemos crear vistas monolíticas para evitar penalizaciones de diseños anidados cuando no hay otras opciones. Cree su interfaz de usuario de la manera correcta y aborde los problemas de rendimiento solo cuando realmente sucedan.
  • Trate su código de UI como un ciudadano de primera clase. Comience cualquier característica construyendo su interfaz de usuario primero, pídale a su diseñador que la revise lo antes posible y pídale a su equipo que la revise de forma independiente.

Espero que estos consejos te ayuden con tu próxima aventura de Android. Con Carlos Palacin Rubio del equipo de Android Groupon.