MVI con máquina de estado. Herramientas | de Nikolái Kotchetkov | Medio

Parte II Abstracciones prácticas para entrometerse y codificar«Estrecho de aprendizaje» sobre la adopción de la arquitectura State-Machine MVI por Ulyana Kotchetkova Esta es la segunda parte de la serie «MVI con State-Machine» que describe algunas herramientas útiles y abstracciones para organizar su código. Consulte las otras partes de la serie para conocer los pasos básicos y cómo implementar aplicaciones de varios módulos: el código fuente del artículo y la biblioteca central muy básica están disponibles en GitHub. La biblioteca es completamente opcional, en caso de que quiera ahorrar algo de tiempo si no escribe su propio núcleo. Los fragmentos de código de este artículo se basan en el módulo de inicio de sesión de la muestra avanzada: «Aplicación de bienvenida». Veremos la estructura completa de la aplicación en la Parte III de la serie.

Contenidos

Abstracciones prácticas

En el ejemplo simple del artículo anterior, todo el trabajo fue realizado por los objetos de estado de la máquina. Ha hecho lo siguiente: – ejecutar una «operación de red» – renderizar datos de estado – creación del siguiente estado Esa es una responsabilidad bastante grande, que podría no ser tan grande en términos de acoplamiento y prueba. Introduzcamos algunas abstracciones que exoneran al Estado.

casos de uso

Como caso de uso, asumo cualquier lógica comercial implementada fuera de su lógica de vista en un estado. Ya sea una operación de red o cualquier otro «caso de uso», póngalo a disposición de su estado y utilícelo como desee. Nada nuevo aquí: estoy seguro de que ya usa el enfoque en su tipo de arquitectura limpia o similar. Para ver un ejemplo del uso de un caso de uso externo, consulte el ejemplo de Bienvenida: Inyectar un caso de uso en el estado de la máquina

Procesador de estado de la interfaz de usuario

Preparar el estado de la interfaz de usuario compleja a partir de sus datos de estado puede ser una tarea no trivial en aplicaciones de interfaz complejas. Puede ser una buena idea alejar algunos acoplamientos para ver el estado y las estructuras de datos de su lógica de estado. Probar la creación exacta del estado de la vista sería mucho más fácil si lo hiciera como una función más o menos limpia. Representador de estado de la interfaz de usuario Otro punto es que los estados de su máquina pueden compartir la misma lógica de representación, por lo que externalizar el representador jugaría un papel importante en términos de reutilización de código. Por ejemplo, PasswordEntryState y ErrorState del ejemplo de bienvenida utilizan la misma representación de estado de vista. Puede poner su renderizador en una fábrica estatal u obtenerlo del contexto general (ver más abajo).

Paso de datos y despliegue de dependencias

La creación explícita de nuevos estados para luego pasarlos a la máquina de estados (como en el ejemplo básico) generalmente no es una buena idea en términos de proporcionar acoplamientos y dependencias. El estado de la máquina, cuando se crea, puede requerir tres clases principales de dependencias:

  • Datos de estados cruzados, p. B. Datos cargados en un estado anterior, estado de datos compartidos, etc. Los datos entre estados varían mucho de una transición a otra y la mayoría de las veces no se pueden proporcionar de una vez por todas.
  • Dependencias específicas del estado, como los casos de uso que opera el estado. Podría proporcionarse una vez por instancia de ensamblado de máquina de estado (estático).
  • Dependencias comunes para todos los estados de la máquina: renderizadores, proveedores de recursos, fábricas. También podría proporcionarse de forma estática.

Eres libre de elegir la forma en que implementas las dependencias, pero echemos un vistazo al enfoque que se me ocurrió que funciona bien tanto para la implementación de dependencias como para las pruebas/simulacros.

Datos interestatales

Para datos interestatales, acepto todos los datos dinámicos que se transmiten entre estados. Puede ser producto de cálculos, datos generados por el usuario, etc. Para mantener limpia nuestra API estatal y promover la inmutabilidad, pasamos los datos interestatales al constructor estatal: Interstate Data Passing

Dependencias específicas del estado

Para proporcionar dependencias específicas para cada clase de estado en particular, sugiero usar fábricas de estado dedicadas inyectadas en su marco DI. Tomemos el ejemplo de caso de uso anterior y ampliémoslo con una fábrica estatal: Fábrica estatal dedicada

Dependencias comunes a todos los estados de una máquina de estado

Las dependencias comunes pueden incluir renderizadores, generadores de estado, interfaces externas comunes y cualquier otra cosa que requieran todos los estados que componen la máquina de estado. Para simplificar y ahorrar la cantidad de parámetros del constructor, propongo vincularlos a una interfaz común y exponerlos como un todo. Llamémoslo un contexto general: puede proporcionar el contexto para su estado a través de los parámetros del constructor. Para facilitar aún más las cosas, creemos un estado base común para el ensamblaje de la máquina de estado y usemos la delegación para proporcionar cada una de las dependencias de contexto: la propiedad apropiada como si se proporcionara explícitamente: Todas las dependencias proporcionadas

Fábrica Estatal Conjunta

Como mencioné antes, no es una buena idea crear explícitamente nuevos estados para pasarlos a la máquina de estados (como en el ejemplo simple) en términos de proporcionar acoplamientos y dependencias. Alejémoslo de nuestros estados de máquina introduciendo una interfaz de fábrica común que asume la responsabilidad de proporcionar dependencias y abstraer nuestra lógica de creación de estado: Interfaz de fábrica de estado común Cualquier método de fábrica aquí acepta solamente los datos interestatales dinámicos. Las dependencias estáticas para la instancia de la máquina de estado (dependencias específicas de contexto y estado) se proporcionan implícitamente. Esto desacopla la lógica de estado de las implementaciones concretas, lo que reduce el acoplamiento y mejora en gran medida nuestra capacidad de prueba. La implementación de fábrica exacta que une todos los datos y dependencias podría verse así: Implementación de fábrica de estado de inicio de sesión La fábrica está disponible para los estados de su máquina a través del contexto común, desvinculando efectivamente sus estados de los demás: use fábrica para usar un Para diferente crear estado Podríamos burlarnos de la fábrica en nuestras pruebas y examinar las transiciones de estado a fondo: Burlarse de una fábrica de estado También podemos exponer la fábrica de estado al ViewModel y usarlo para inicializar la máquina de estado: Inicializar una máquina de estado con la fábrica de estado

Ver la gestión del ciclo de vida con FlowStateMachine

Imagine que tenemos una operación intensiva en recursos como el seguimiento de ubicación en nuestro estado. Puede ahorrar los recursos del cliente si dejamos de rastrear cuando la vista está inactiva: la aplicación pasa a segundo plano o la actividad de Android está en pausa. En este caso, sugiero crear gestos especiales y pasarlos a la máquina de estado cada vez que cambie el estado del ciclo de vida. Por ejemplo, FlowStateMachine que usamos en la Parte I exporta la propiedad uiStateSubscriptionCount, que es un flujo de la cantidad de suscriptores que escuchan la propiedad uiState. Si usa algunas funciones repeatOnLifecycle o similares para suscribirse a uiState, puede usar esta propiedad para crear su manejo especial de eventos de ciclo de vida. Como recordatorio, repeatOnLifecycle deja de capturar el flujo cuando el ciclo de vida de la vista está en pausa y lo reanuda cuando se reanuda. Para mayor comodidad, hay disponible una función de extensión mapUiSubscriptions para aplanar el texto repetitivo. Acepta dos funciones de generación de gestos y actualiza la máquina de estado con ellas cuando cambia el estado del participante:

Conclusión

Las herramientas descritas en este artículo pueden ayudarlo a implementar dependencias, desacoplar los estados de su máquina y mejorar la capacidad de prueba. Los ejemplos proporcionados aquí son solo una sugerencia e ilustran un posible enfoque para organizar su código. Como dije en la Parte I de la serie, la arquitectura pretende ser lo más mínima e imparcial posible, lo que le permite elegir la forma en que maneja la estructura de su aplicación de la manera que desee. No obstante, los patrones de estructuración de código y las herramientas que se describen en esta parte funcionan muy bien para mí y me ayudan a organizar flujos complejos de pantallas múltiples. Es común hoy en día dividir su aplicación en módulos de biblioteca independientes. Pasemos a la Parte III para ver cómo podemos usar múltiples módulos y múltiples plataformas con la máquina de estado.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.