Cómo elegir la arquitectura iOS adecuada (Parte 2)

MVC, MVP, MVVM, VIPER o VIP

Puedes consultar la primera parte aquí.

Las principales arquitecturas de iOS

Una breve reseña.

MVC

Las capas MVC son las siguientes:

M: lógica de negocios, capa de red y capa de acceso a datos

V: UI Layer (cosas UIKit, Storyboards, Xibs)

C: coordina la mediación entre el modelo y la vista.

Para entender MVC debemos entender el contexto en el que se inventó. MVC se inventó en los viejos días de desarrollo web, donde Views no tiene estado. En los viejos tiempos, cada vez que necesitamos un cambio visual en el sitio web, el navegador vuelve a cargar todo el HTML. En ese momento no había un concepto del estado de la vista que se mantenía y guardaba.

Hubo, por ejemplo, algunos desarrolladores que se mezclaron dentro del mismo archivo HTML, PHP y acceso a la base de datos. Entonces, la principal motivación de MVC fue separar la capa de Vista de la capa de Modelo. Esto aumentó la capacidad de prueba de la capa Modelo. Supuestamente en MVC, la capa Vista y Modelo no debería saber nada el uno del otro. Para que esto sea posible, se inventó una capa intermedia llamada Controlador. Este fue el SRP que se aplicó.

Un ejemplo del ciclo MVC:

  1. Se dispara una acción / evento del usuario en la capa de visualización (por ejemplo: Actualizar acción) y esa acción se comunica al controlador
  2. El controlador que solicita datos a la capa modelo
  3. Modele los datos de devoluciones al controlador
  4. El controlador dice que para la vista actualizar su estado con los nuevos datos
  5. Ver actualización de su estado

Apple MVC

En iOS, el controlador de vista está acoplado al UIKit y la vista del ciclo de vida, por lo que no es MVC puro. Sin embargo, en la definición de MVC, no hay nada que decir que el Controlador no puede conocer la implementación específica de Vista o Modelo. Su objetivo principal es separar las responsabilidades de la capa Modelo de la capa Vista para que podamos reutilizarla y probar la capa Modelo de forma aislada.

El ViewController contiene la Vista y posee el Modelo. El problema es que usamos para escribir el código del controlador, así como el código de vista en el ViewController.

MVC a menudo crea el problema llamado Massive View Controller, pero eso solo sucede y se convierte en algo serio en aplicaciones con suficiente complejidad.

Hay algunos métodos que el desarrollador puede usar para hacer que el Controlador de vista sea más manejable. Algunos ejemplos:

  • Extraer la lógica de VC para otras clases como la fuente de datos de métodos de vista de tabla y delegar para otros archivos usando el patrón de diseño delegado.
  • Cree una separación más clara de responsabilidades con la composición (por ejemplo, divida el VC en controladores de vista secundarios).
  • Use el patrón de diseño del coordinador para eliminar la responsabilidad de implementar la lógica de navegación en el CV
  • Utilice una clase de contenedor DataPresenter que encapsule la lógica y transforme el modelo de datos en una salida de datos que represente los datos presentados al usuario final.

MVC vs MVP

Cómo puedes ver el diagrama de MVP es muy similar a MVC

El MVC fue un paso adelante, pero todavía estuvo marcado por la ausencia o el silencio sobre algunas cosas.

Mientras tanto, la World Wide Web creció y evolucionaron muchas cosas en la comunidad de desarrolladores. Por ejemplo, los programadores comenzaron a usar Ajax y solo cargan partes de páginas en lugar de la página HTML completa a la vez.

En MVC, creo que no hay nada que indique que el Controlador no debe conocer la implementación específica de View (ausencia).

HTML era parte de la capa de Vista y muchos casos eran tontos. En algunos casos, solo recibe eventos del usuario y muestra el contenido visual de la GUI.

A medida que partes de las páginas web comenzaron a cargarse en partes, esta segmentación condujo en la dirección de mantener el estado de visualización y una mayor necesidad de una separación de responsabilidad de lógica de presentación.

La lógica de presentación es la lógica que controla cómo se debe mostrar la IU y cómo interactúan los elementos de la IU. Un ejemplo es la lógica de control de cuándo un indicador de carga debería comenzar a mostrarse / animarse y cuándo debería dejar de mostrarse / animarse.

En MVP y MVVM, la capa de vista debe ser tonta sin ninguna lógica o inteligencia, y en iOS, el controlador de vista debe ser parte de la capa de vista. El hecho de que View sea tonto significa que incluso la lógica de presentación permanece fuera de la capa View.

Uno de los problemas de MVC es que no está claro dónde debería permanecer la lógica de presentación. Él simplemente está en silencio sobre eso. ¿Debería estar la lógica de presentación en la capa de Vista o en la Capa de modelo?

Si la función del modelo es simplemente proporcionar los datos "en bruto", significa que el código en la Vista sería:

Considere el siguiente ejemplo: tenemos un usuario, con nombre y apellido. En la Vista, debemos mostrar el nombre de usuario como "Apellido, Nombre" (por ejemplo, "Flores, Tiago").

Si la función del Modelo es proporcionar los datos "en bruto", significa que el código en la Vista sería:

deje firstName = userModel.getFirstName ()
deje lastName = userModel.getLastName ()
nameLabel.text = lastName + "," + firstName

Esto significa que sería responsabilidad de la Vista manejar la lógica de la interfaz de usuario. Pero esto hace que la lógica de la interfaz de usuario sea imposible de realizar pruebas unitarias.

El otro enfoque es hacer que el Modelo exponga solo los datos que deben mostrarse, ocultando cualquier lógica comercial de la Vista. Pero luego, terminamos con Modelos que manejan tanto la lógica de negocio como la de UI. Sería comprobable por unidad, pero luego el Modelo termina, implícitamente dependiente de la Vista.

let name = userModel.getDisplayName ()
nameLabel.text = nombre

El MVP es claro al respecto y la lógica de presentación permanece en la capa de presentador. Esto aumenta la capacidad de prueba de la capa Presentador. Ahora el modelo y la capa del presentador son fácilmente comprobables.

Normalmente en las implementaciones de MVP, la Vista está oculta detrás de una interfaz / protocolo y no debe haber referencias al UIKit en el Presentador.

Otra cosa a tener en cuenta son las dependencias transitivas.

Si el controlador tiene una capa empresarial como dependencia y la capa empresarial tiene una capa de acceso a datos como dependencia, entonces el controlador tiene una dependencia transitiva para la capa de acceso a datos. Dado que las implementaciones de MVP normalmente usan un contrato (protocolo) entre todas las capas, no tiene dependencias transitivas.

Las diferentes capas también cambian por diferentes razones y a diferentes velocidades. Entonces, cuando cambia una capa, no desea que esto cause efectos / problemas secundarios en las otras capas.

Los protocolos son más estables que las clases. Los protocolos no tienen detalles de implementación y con los contratos, por lo que es posible cambiar los detalles de implementación de una capa sin afectar las otras capas.

Entonces los contratos (protocolos) crean un desacoplamiento entre las capas.

MVP vs MVVM

Diagrama MVVM

Una de las principales diferencias entre MVP y MVVM es que en MVP el presentador se comunica con la Vista a través de interfaces, y en MVVM la Vista está orientada a cambios de datos y eventos.

En The MVP hacemos un enlace manual entre Presenter y View usando interfaces / protocolos.
En The MVVM hacemos enlaces de datos automáticos usando algo como RxSwift, KVO o usamos un mecanismo con genéricos y cierres.

En el MVVM ni siquiera necesitamos un contrato (por ejemplo: interfaz java / protocolo iOS) entre ViewModel y View porque generalmente nos comunicamos a través del Patrón de diseño del observador.

El MVP usa el Patrón de delegado porque la Capa de presentador delega los pedidos a la Capa de vista, por lo que necesita saber algo sobre la Vista, incluso si es solo la firma de interfaz / protocolo. Piense en la diferencia entre Notification Center y TableView Delegates. El Centro de notificaciones no necesita interfaces para crear un canal de comunicación, pero TableView Delegates usa un protocolo que las clases deberían implementar.

Piense en la lógica de presentación de un indicador de carga. En MVP, el presentador hace ViewProtocol.showLoadingIndicator. En MVVM puede haber una propiedad isLoading en ViewModel. La capa de Vista a través de un enlace de datos automático detecta cuándo esta propiedad cambia y se actualiza. MVP es más imperativo que MVVM porque el presentador da órdenes.

MVVM tiene más que ver con los cambios de datos que con los pedidos directos, y hacemos asociaciones entre los cambios de datos y ver las actualizaciones. Si utilizamos RxSwift y el paradigma de programación reactiva funcional junto con MVVM, hemos hecho que el código sea aún menos imperativo y más declarativo.

MVVM es más fácil de probar que MVP porque MVVM utiliza el patrón de diseño de observador que transfiere datos entre componentes de forma desacoplada.
Por lo tanto, podemos probar simplemente observando los cambios en los datos simplemente comparando los dos objetos en lugar de crear simulacros de las llamadas a métodos para probar la comunicación entre View y Presenter.

PD: Hice algunas actualizaciones al artículo que lo hicieron crecer mucho, por lo que fue necesario dividirlo en tres partes. Puedes leer la tercera parte aquí.

La segunda parte termina aquí. Todos los comentarios son bienvenidos. La tercera parte hablará sobre VIPER, VIP, programación reactiva, compensaciones, restricciones y sentido contextual.

¡Gracias por leer! Si te gustó este artículo, aplaude
para que otras personas puedan leerlo también :)