Patrón de diseño de repositorio en Swift

Una forma limpia de consultar sus modelos

¿Qué problema soluciona?

Si necesita consultar sus objetos de modelos de diferentes lugares en su código una y otra vez, un repositorio puede ser realmente útil para proporcionar un punto de entrada única de trabajar con sus modelos y quitar código de consulta duplicado. Puede llevarlo aún más lejos y usarlo con protocolos, de esta manera puede cambiar fácilmente implementaciones (por ejemplo, para pruebas unitarias) o puede usarlo con genéricos para hacer una abstracción genérica más * drum roll *. En este artículo, cubriré todos estos casos.

Dibujando la escena.

Supongamos que tiene un código que obtiene datos de una API y lo asigna a objetos de modelo. En este ejemplo, buscaré una lista de artículos de un servidor.

Esto puede parecer un poco extraño, pero es solo RxSwift, usando Moya como la capa de abstracción de redes, pero eso realmente no importa para entender lo que está sucediendo. La forma en que recupere sus datos depende totalmente de usted.

Esta pieza de código hace

  1. Una petición GET al servidor
  2. Asigna el JSON devuelto a una matriz de objetos Article
  3. Se llama al cierre cuando se realiza todo el trabajo.

¿Por qué necesitamos un repositorio?

Bueno, por el momento no lo hacemos. Si solo llama a la API una vez en toda su base de código, agregar un repositorio podría ser excesivo (o, como algunos pueden decir, ingeniería excesiva).

Ok ... pero ¿cuándo es conveniente usar un objeto de repositorio?
Supongamos que su base de código comienza a crecer y necesita escribir el código para recuperar los artículos una y otra vez. Puede decir "copiemos el código y pegámoslo donde sea necesario para buscar todos los artículos".

No se hizo daño, nadie murió. ¿Correcto?

En ese momento, una gran alarma roja debería comenzar a parpadear en sus cerebros.

Hola repositorio.

Un repositorio es solo un objeto que encapsula todo el código para consultar sus modelos en un solo lugar, por lo que tiene un punto de entrada único si lo desea, p. Obtenga todos los artículos.

Creemos un objeto de repositorio que proporcione una API pública para obtener los artículos.

Ahora podemos llamar a este método y no tenemos que preocuparnos por lo que sucede detrás de escena para obtener los artículos reales.
Simplemente llame al método y obtendrá los artículos. Bien, verdad?
¡Pero espera, hay más!

Manejar todas las interacciones de artículos

Podemos usar el repositorio para agregar más métodos para interactuar con nuestro objeto modelo. La mayoría de las veces que quieren hacer CRUD (crear, leer, actualizar, eliminar) las operaciones de su modelo. Bueno, sólo tiene que añadir la lógica para estas operaciones en el repositorio.

Esto hace un buen uso de la API a través de su código, sin tener que repetir el mismo código una y otra vez.

En la práctica, el uso de un repositorio se vería así.

Bastante agradable y legible, ¿verdad? Pero espera, esto se pone aún mejor.

Encendido: protocolos

En el código anterior, siempre utilicé el ejemplo de "obtener datos de una API". Pero, ¿qué sucede si necesita agregar soporte para cargar datos desde un archivo JSON local en lugar de una fuente en línea?

Bueno, si crea un protocolo que enumera los nombres de los métodos, puede crear una implementación para la API en línea y otra para desconectar los datos.

Esto podría verse así.

Un protocolo solo dice "si te conformas, debes tener las firmas de estos métodos, ¡pero no me importa la implementación real!"

Eso es genial, puedes crear un WebArticleRepository y un LocalArticleRepository. Ambos tendrán todos los métodos que figuran en el protocolo, pero puede escribir 2 implementaciones totalmente diferentes.

Encendido: Prueba de unidad

El uso de protocolos también es realmente conveniente cuando desea probar unitariamente su código, porque puede crear otro objeto que implemente el protocolo de repositorio, pero en su lugar devuelva datos simulados.

Si usa esto junto con la inyección de dependencia, hace que sea realmente fácil probar un objeto específico.

Un ejemplo

Supongamos que tiene un modelo de vista, y el modelo de vista obtiene sus datos a través de un repositorio.

Si desea probar el modelo de vista, debe seguir los artículos que se obtendrán de la web.
Esto en realidad no es lo que queremos. Queremos que nuestra prueba sea determinista tanto como sea posible. En este caso, los artículos recuperados de la web podrían cambiar con el tiempo, no podría haber conexión a Internet en el momento en que se ejecuten las pruebas, el servidor podría estar inactivo, ... estos son todos los escenarios posibles en los que nuestras pruebas fallarían, porque son fuera de nuestro control. Y cuando probamos, queremos / necesitamos tener el control.

Afortunadamente, en realidad es muy simple resolver esto.

Hola, inyección de dependencia.

Solo necesita establecer la propiedad articleRepo a través del inicializador. El caso predeterminado será el que desee para su código de producción y cuando escriba una prueba unitaria, puede cambiar el repositorio con su versión simulada.

Pero tal vez estás pensando, ¿y los tipos? Un WebArticleRepository no es un MockArticleRepository, entonces ¿el compilador no se quejará? Bueno, no si usas el protocolo como un tipo. De esta forma, le informamos al compilador que permita todo siempre que se ajuste al protocolo ArticleRepository (lo que hacen tanto el Web como el MockArticleRepository).

El código final se vería así.

Y en su prueba de unidad, podría cambiarlo de esta manera.

Ahora tiene control total sobre los datos que devuelve su repositorio.

Super potenciador: genéricos

Podría llevar esto aún más lejos, utilizando genéricos. Si lo piensa, la mayoría de los repositorios siempre tienen las mismas operaciones

  1. obtener todas las cosas
  2. obtener algunas de las cosas
  3. inserta algunas cosas
  4. borrar cosa
  5. actualizar una cosa

Bueno, lo único que es diferente es la palabra "cosa", por lo que este podría ser un excelente candidato para usar un protocolo con genéricos. Puede sonar complicado, pero en realidad es bastante simple de hacer.

Primero cambiaremos el nombre del protocolo a Repository, para hacerlo más ... genérico .
Y luego eliminaremos todos los tipos de artículos, y los reemplazaremos por la T mágica. Pero la letra T es solo un reemplazo de ... cualquier cosa que queramos que sea. Solo necesitamos marcar T como el tipo asociado del protocolo.

Así que ahora podemos usar este protocolo para cualquier objeto modelo que tengamos.

1. Repositorio de artículos

El compilador inferirá el tipo de T al artículo, porque al implementar los métodos, hemos especificado qué es T. En este caso, un artículo objeto.

2. Depósito de usuarios

Eso es.

Espero que hayas disfrutado el artículo y si tienes alguna pregunta o comentario, solo pregúntales a continuación o comunícate conmigo en Twitter y hablemos.