PDA

Ver la Versión Completa : [ TUTORIAL ] Patrones de diseño y de arquitectura


vAlmaraz
14/05/16, 20:51:06
Buenas,

Para todo el que le interese aprender sobre patrones de diseño y arquitectura, aporto un artículo en mi blog sobre el patrón de repositorios y un repositorio en GitHub que he creado con un proyecto Android simple para aprender a aplicar MVP en apps.

Enlaces:
http://www.valmaraz.com/blog-41-patron-de-repositorios-desarrollo-de-software
https://github.com/vAlmaraz/mvp-android

Para los que no tengan mucha experiencia aplicando patrones, convendría empezar por el de repositorios. Una vez familiarizado con él, resulta más fácil pasar a otros patrones, como MVP y Clean Architecture.

En el README del repositorio hay una explicación (en inglés) de conceptos básicos, pasos previos, etc para aprender a sobre arquitectura de software.

Es totalmente convertible o traducible para iOS:
- Activities por ViewControllers
- Adapters por DataSources y Delegates
- Retrofit por AFNetworking
- Application por AppDelegate
- ....

Iré completando y mejorando el proyecto para hacerlo más completo y con una interfaz más atractiva.

Cualquier duda, estoy disponible dentro de las posibilidades de mi tiempo libre, tanto por aquí como en issues en el repositorio.
Se agradecen estrellas en el repo!

Un saludo!

mocelet
15/05/16, 10:49:26
Qué bueno, está muy bien concienciar sobre el uso de patrones y buenas prácticas, así como de las guías de estilo de código para mejorar la legibilidad y mantener coherencia.

Muchos de todas formas se deberían llamar "patrón sentido común si quieres añadir funcionalidad más adelante" o "patrón sentido común del uso de interfaces que están para algo".

A veces me recuerdan a las normas de convivencia que tuvieron que redactar en mi comunidad de vecinos con cosas como "no tiren basura al patio común": si hay que dejar por escrito esas cosas es que hay un problema de fondo más grave, normalmente de falta de empatía (otros vecinos que sufren tu dejadez u otros desarrolladores que tengan que modificar algo o incluso uno mismo en un futuro cuando ya no te acuerdas de qué hiciste y quieres cambiar algo).

Dexafree
15/05/16, 11:05:56
La mayoría de artículos sobre MVP y CleanArchitecture están en ingles, cosa que a algún que otro desarrollador español puede suponerle una barrera de entrada (aunque el 99% del material y documentación sobre programación que existe es e inglés...) así que buen trabajo traduciendo recursos al español :ok:

Desde hace un año aproximadamente trabajo con la arquitectura MVP-I (Model-View-Presenter-Interactor), y desde luego ha mejorado muchísimo el desacople de mi código.

En algunos proyectos además utilizo RxJava, que aunque al principio cueste un poco cogerle el "flow", acaba siendo bastante cómodo para concatenación de operaciones, transformaciones de recursos recibidos de APIs...

Desde luego aprender a utilizar patrones y diseñar arquitecturas es un paso adelante para cualquier desarrollador, no solo de Android, sino en general.


PD: En el subreddit de /r/androiddev hay muchísimos artículos de este tipo. Dagger, nuevas libs que van apareciendo, artículos sobre arquitecturas... https://es.reddit.com/r/androiddev

"patrón sentido común del uso de interfaces que están para algo".

Esa me la apunto, grande xD

mocelet
15/05/16, 13:30:50
Desde hace un año aproximadamente trabajo con la arquitectura MVP-I (Model-View-Presenter-Interactor), y desde luego ha mejorado muchísimo el desacople de mi código.

O_O Desconocía el término, en efecto parece el caso de "uso de interfaces que para eso están".

Los programas de ajedrez de los 80 y 90 tenían un protocolo basado en texto para comunicarse con la implementación de la interfaz de usuario, que podía ser línea de comandos, gráfica, de ventanas, e incluso de sistemas operativos diferentes. La lógica no había que tocarla, y la capa de presentación simplemente le comunicaba a la lógica eventos (ha movido aquí) o los recibía (mueve tal pieza). Creo que incluso algunos motores programados en C en su día se integran con apps móviles hoy día que únicamente aportan la capa de visualización.

Dexafree
15/05/16, 17:09:39
También ayuda muchísimo a la escritura de tests, ya que toda la parte de "Presenter" puedes escribirla en Java puro, con lo cual los tests se pueden ejecutar con JUNIT, sin tener que recurrir a tests de instrumentación (lo que conlleva a arrancar un emulador y ejecutarlos).

Eso + Dagger2 es amor xD

vAlmaraz
15/05/16, 19:37:48
[...]

Desde hace un año aproximadamente trabajo con la arquitectura MVP-I (Model-View-Presenter-Interactor), y desde luego ha mejorado muchísimo el desacople de mi código.

[...]

Desde luego aprender a utilizar patrones y diseñar arquitecturas es un paso adelante para cualquier desarrollador, no solo de Android, sino en general.

[...]




Si, la verdad es que solo separando la capa de acceso a datos, la legibilidad y calidad del código mejora mucho.
Si además divides en más capas las implementaciones de las vistas...

Ojalá hubiera empezado antes a aplicar arquitectura. Me hubiera ahorrado mucho tiempo con cambios imprevistos. Además que el patrón de repositorios y las capas independientes del framework, a la hora de hacer una app para Android e iOS, es un simple copia y pega traduciendo el lenguaje.

manolazo
24/05/16, 01:04:45
Buenas
Me he descargado el proyecto desde github https://github.com/vAlmaraz/mvp-android para echarle un ojo pero no funciona , se queda la pantalla principal con un boton de retry .

He mirado el codigo parece bastante enrevesado al menos para mi nivel , realmente no veo la sencillez del modelo (anotaciones NONNull pasandolas por parametro en las funciones, uso de librerias retrofit2, etc ).

Hasta que punto es mejor usar este modelo al modelo tradicional para este tipo de apps? Supongo que sera un ejemplo sencillo y que lo ideal seria usarlo para proyectos complejos?

Esta claro que vosotros estais a un nivel de programacion muy por encima de la media que del foro y lo veis de otra manera , pero bueno, de ahi mis dudas.

mocelet
24/05/16, 11:39:47
Hasta que punto es mejor usar este modelo al modelo tradicional para este tipo de apps? Supongo que sera un ejemplo sencillo y que lo ideal seria usarlo para proyectos complejos?

En el fondo sigue siendo el modelo de siempre (MVC) metiendo interfaces por medio para que el código que trata con la vista (layouts, textviews, botones...) esté perfectamente aislado (desacoplado) de la lógica de tu aplicación.

O, en otras palabras, que la lógica de tu aplicación esté en una clase independiente y no veas ni una sola referencia a cosas de la vista por ninguna parte. Así, si algún día cambias la vista, tu lógica no cambia. Y si tienes que cambiar la lógica, será más fácil.

Ejemplo muy básico, tienes una app que permite actualizar su contenido online y mezclado con el código que descarga cosas tienes un textView.setText("Actualizando") y un textView.setVisibility(View.VISIBLE).

Lo primero que te dice el sentido común a poco que empieces a cambiar cosas en el código es crear un método en la misma clase que se llame mostrarProgreso() y ahí ya meter el setText y el setVisibility. Al menos ahora el método que descarga cosas no tiene referencias feas a textviews y si algún día quieres cambiar el textview por un progressbar o por una animación súper chula solo tienes que tocar en un sitio.

Sin embargo, la clase sigue teniendo código de lógica o de funcionalidad y código de visualización. La siguiente idea feliz es separarlos. En una clase la lógica y en otra el actualizar la vista. Para que la lógica le pueda decir a la capa de presentación que muestre el mensaje de actualización es necesario que la capa de presentación implemente una interfaz que tenga el método mostrarProgreso()

Y esa es la base del MVP, añadir una capa de abstracción para que la lógica sólo indique QUÉ hay que mostrar, el CÓMO ya lo decidirá la capa de presentación.

Idem para las acciones del usuario (interacciones), en vez de tratar directamente el evento de un botón con onActualizar(View v) por ejemplo, tu lógica implementa el método actualizar() y otra clase se encarga de llamarlo al recibir el evento del usuario (que puede ser de un botón o igual mañana es un gesto de deslizar, a tu lógica le da igual). En este caso lo que le importa a la lógica es QUÉ acción es (actualizar), no CÓMO se generó (se apretó un botón de nombre tal).

Los modelos, patrones, arquitecturas, etc. no son más que recomendaciones o buenas prácticas. Al final todas van dirigidas a que el código sea más legible, más manejable, menos propenso a errores y más extensible. En definitiva, más intuitivo. Qué aplicar en cada caso ya queda a criterio del desarrollador, desacoplar por desacoplar no tiene sentido (por eso los ejemplos sencillos son desafortunados, da la idea de que es matar moscas a cañonazos).

Con las funciones de refactorización que tiene Android Studio tampoco se tarda mucho en extraer una interfaz y empezar a desacoplar funcionalidad.

Dexafree
24/05/16, 11:40:01
He mirado el codigo parece bastante enrevesado al menos para mi nivel , realmente no veo la sencillez del modelo

El modelo no tiene por qué ser sencillo ni complejo, sino fácilmente desgranable en partes más pequeñas que permitan tratarlas de forma independiente. Esto no es propio de Android, sino buena práctica del desarrollo en general.

También es cierto que utilizar este tipo de arquitecturas no es lo recomendable cuando simplemente estás aprendiendo, ya que la arquitectura termina siendo una forma de organizar tu código una vez has superado la barrera de entrada inicial (saber cómo obtener los resultados que deseas). Al final al usuario le da igual si lo tienes todo en una activity de 10000 líneas o si lo has separado utilizando una arquitectura hexagonal, el solo quiere que al darle al botón se vea lo que se tenga que ver.


anotaciones @NONNull pasandolas por parametro en las funciones

No es que esté pasando la anotación por parámetro, sino que la anotación está asociada al parámetro.
Esa anotación puede estar asociada a métodos que nunca pueden devolver null, o a parámetros que nunca pueden ser null.

uso de librerias retrofit2

El uso de esta librería es bastante común cuando se habla de hacer peticiones sobre HTTP, y además es bastante cómoda de utilizar. Es de las más utilizadas en la comunidad Android, te recomiendo que si algún día necesitas hacer peticiones de red le eches un ojo :ok:

Hasta que punto es mejor usar este modelo al modelo tradicional para este tipo de apps?

Como he puesto arriba,


También es cierto que utilizar este tipo de arquitecturas no es lo recomendable cuando simplemente estás aprendiendo, ya que la arquitectura termina siendo una forma de organizar tu código una vez has superado la barrera de entrada inicial


Además, no es algo que se entienda en una leída de código, hay que pegarse con el hasta que tienes un poco el "momento ajá!" y entiendes por qué se hace cada cosa.
Ten en cuenta también que una arquitectura no es un estándar, cada uno puede adaptarla a sus necesidades.


---

Dicho esto, paso a hacer un par de comentarios sobre el código... Como siempre, la programación es el mundo del cuñadismo por excelencia (aka: "Pues yo esto lo haría de otro modo...")

1. Clase Network. Cada vez recreas un RetrofitService, cosa que es bastante costosa (ya que se hace por reflection). Te recomiendo que lo cachees y lo reutilices (ej: singleton, por ejemplo).
La aproximación que suelo utilizar en esos casos es utilizar Dagger2 e inyectarlo donde se necesite (lo mismo con el OkHttpInterceptor)

2. Retrofit. Generas la URL directamente desde código. Te recomiendo que le eches un ojo a las anotaciones de Retrofit, ya que puedes pasarle directamente los parámetros y el automáticamente te hace las concatenaciones: http://square.github.io/retrofit/ (apartado URL Manipulation).

3. getContext en las vistas. En tu caso estás utilizando la propia vista como contexto. Ten cuidado, ya que eso puede llevar a que tengas memory leaks. Siempre que necesites un Context, deberías intentar llamar a .getApplicationContext, ya que ese contexto siempre está disponible mientras viva la aplicación y no está ligado a ninguna activity en concreto. Ya lo que sería genial sería que fuera parte del grafo de dependencias, y así podrías dejar la interfaz de la vista totalmente libre de Android, cosa que si planeas hacer tests para el presenter será de gran ayuda, ya que podrás ejecutarlos directamente desde JUnit.

Esas son las primeras opiniones después de echarle un ojo rápido al código. Espero que no te molesten!

manolazo
26/05/16, 00:25:19
Esta claro que es otra forma de trabajar y sobre todo de tener los proyectos mas organizados , el tema es como dice Mocelet que para cosas sencillas yo lo veo como matar moscas a cañonazos y quieras o no , para alguien que programa por hobby como yo habiendo aprendido de forma autodidacta desde casa a base de pdfs , libros y tutoriales y que en casi ninguno usen este modelo de organizacion pues cuesta un poco cambiar el chip.

De todas formas de lo que se trata es de seguir aprendiendo y echarle un ojo a este modelo porque al menos en mi caso soy un poco desastre ya que programo de forma un poco desordenada , empiezo con algo sencillo y poco codigo al que voy añadiendo mas funciones y mas clases a medida que voy agrandando el proyecto y al final se convierte en algo que es dificil de mantener y actualizar. La verdad que cuando hay tanto codigo no se como organizarlo.

La verdad que da gusto leeros a los PROS del foro (Kriogen, mocelet, dexafree...) se aprende bastante con vosotros y haceis una gran labor resolviendo las dudas que os planteamos.

Saludos.

mocelet
26/05/16, 13:40:43
soy un poco desastre ya que programo de forma un poco desordenada , empiezo con algo sencillo y poco codigo al que voy añadiendo mas funciones y mas clases a medida que voy agrandando el proyecto y al final se convierte en algo que es dificil de mantener y actualizar. La verdad que cuando hay tanto codigo no se como organizarlo.

¿Acaso se puede empezar de otra forma? Empezar con algo sencillo e ir ampliando es lo habitual salvo que tengas un proyecto con una especificación cerrada o todas tus apps tengan el mismo molde. En apps que van refinándose conforme a lo que piden los usuarios, comentarios, ideas nuevas e incluso lo que tú mismo vas aprendiendo con la experiencia, no queda otra.

Da igual el nivel que tengas, en cuanto pase un tiempo el código anterior te hará daño a la vista y además pensarás "esto ahora lo haría de otra forma". A veces porque la propia tecnología cambia y favorece ciertas prácticas, otras simplemente porque nos falta una bola de cristal que te diga cómo va a evolucionar el proyecto.

Y al final, si la app evoluciona mucho, que se convierta en algo difícil de mantener es inevitable. A mí me pasa con el cuatro en raya que lleva cinco años ya... creo que las únicas líneas que se mantienen de siempre son unas pocas líneas muy feas de la inteligencia artificial que funcionan muy bien y "mejor no tocar". El resto, reescrito más de una vez.

Curiosamente hace poco reescribí el online aplicando el modelo MVP sin saber su nombre xD Quería separar completamente el comportamiento online de la gestión de sockets y del apartado gráfico en previsión de dejar de usar custom views y pasarme a un motor de juegos tipo libgdx y algún día quitar los sockets y usar websockets. Como también estoy preparando la versión de iOS, si la lógica es independiente de la parte gráfica y de la conexión puedo tener un código que a grandes rasgos sea el mismo en Java y en Swift (cambiando la sintaxis, pero el "pseudocódigo" sería el mismo). La parte específica de cada sistema estará en su clase independiente, más fácil de probar también y de intercambiar (hoy uso una biblioteca de conectividad, mañana otra y al resto de la app no le importa porque la interfaz se mantiene).

Es más código y más interfaces, pero es un caso claro donde merece la pena desacoplar.

vAlmaraz
08/06/16, 07:57:53
Perdonad que no haya contestado antes. He estado bastante liado entre el trabajo y otras cosas.

Respondo a un par de cosas:

El proyecto tal cual no funciona porque la api key para OpenWeather es inválida (no puedo regalar mi api key). Es necesario registrarse y sustituir por una buena.
La api key se cambia en la clase Environment.


Dicho esto, paso a hacer un par de comentarios sobre el código... Como siempre, la programación es el mundo del cuñadismo por excelencia (aka: "Pues yo esto lo haría de otro modo...")

Respecto a las mejoras del código, lo primero muchas gracias por tu opinión, lo agradezco mucho.
Si, Network podría ser un singleton, ya he probado antes con él, pero como siempre dicen que es un antipatrón... de todas formas tengo pendiente hacer pruebas a ver si realmente no merece la pena.
Lo de las anotaciones de Retrofit es como las usaba en el modo tradicional de programar. La ventaja en esta otra forma es que no lleno en una sola interfaz distintas llamadas. Tambien tengo pendiente pensar cómo aprovechar las anotaciones y dividirlas por cada cliente.
Sobre el contexto, toda la razón. El código lo he hecho en ratos libres y corriendo y hay muchas cosas por mejorar.

En global, si, es una arquitectura más compleja para iniciados, pero merece la pena aplicarla siempre, por muy pequeña que sea la app. Con experiencia, os daréis cuenta.

Un saludo!


EDITO: Explico un poco más en detalle la principal razón para no utilizar las anotaciones de Retrofit:
El objetivo es desligar por completo las clases Client de Network (implementación básica de la conexión) de tal manera que ningún Client sepa qué librería se utiliza.
Esto permite que si se decide cambiar de Retrofit a Volley, por ejemplo, el cambio sólo sería en Network, dejando intactos todos los Client.
O un ejemplo más real, si Retrofit se actualiza, como ha pasado de la 1 a la 2, cambiando muchos de los métodos principales y callbacks, sólo afecta a la clase Network.

Sigo teniendo pendiente encontrar una forma de aprovechar mejor las anotaciones, pero por ahora, a falta de tiempo para investigar, eso es lo que se me ha ocurrido para permitir el desacoplamiento.