Haz de tus aplicaciones una roca sólida | Single Responsibility

El principio de Single Responsibility (SRP por sus siglas en Inglés) es la que conocemos por la S de SOLID y comparado con alguno de los demás principios parece muy simple de entender. Sin embargo, es uno de los principios que menos se cumplen.

Al leer la definición uno puede llegar a pensar, esto es algo sencillo, debería hacer que mis «cosas» hagan una y sólo una cosa a la vez. Ahí es donde está nuestro primer malentendido. Este principio dice que dado una porción de código, ésta debería tener una y sólo una razón de cambio.

Quizás estén pensando a qué me refiero exactamente con «una porción de código» en una aplicación Android y, lamento decirles que no hay respuesta simple para eso. SRP aplica a distintos niveles:

Módulos

Imagino que si todavía andan por acá es porque no sólo son desarrolladores Android sino que algo en ustedes (o en sus proyectos) los mantiene atentos a la lectura. Los invito a revisar el diseño de la aplicación en la que estén trabajando ahora mismo, precisamente a sus módulos y los comparen con la siguiente imagen:

Principio de Single Responsibility no aplicado a nivel módulo.

Esta imagen representa una foto de una aplicación Android con algunos módulos. Digamos que nuestra app es para generar facturas para una compañía. Un usuario puede generar una factura, enviársela al cliente, generar reportes para ser analizados por la compañía y también generar reportes para enviar a los clientes indicando cuáles son las facturas hechas durante un período de tiempo determinado.

Sin importar si estás satisfecho o no con la cantidad de módulos, asumamos que este es el diseño que tenemos. Revisando nuestros módulos, encontramos el módulo rest el cual maneja todo lo relacionado a la comunicación con una API externa. ¿Por qué debería cambiar algo en este módulo? Probablemente porque algo relacionado a la comunicación con esa API externa necesite ser cambiado. Deberíamos evitar bajo cualquier contexto incluir cualquier otra cosa en este módulo que no esté relacionado con la comunicación REST. Al final de cuentas, este módulo tiene una y sólo una razón para cambiar.

Por otro lado, el módulo de common parece tener cosas relacionadas con otro módulo. Esto significa que son muchas razones para cambiar por lo que violan SRP.

Caso práctico

La compañía la cual es dueña de esta aplicación tiene un requerimiento nuevo para el equipo de desarrollo. Necesitan hacer un cambio en el reporte que la app envía a nuestros clientes. Para eso, es necesario modificar el módulo de billing. Este módulo tiene la responsabilidad de generar y guardar todas las facturas así también como el envío de todos los reportes a la compañía y a los clientes. ¿No crees que está haciendo muchas cosas? ¿Qué sucedería si necesitamos un nuevo tipo de report? ¿Deberíamos volver a cambiarlo? Bueno… creo que estamos de acuerdo en que son muchas las razones para cambiar este módulo.

Debemos evitar este tipo de problemas en nuestros desarrollos si queremos tener una aplicación que sea escalable y mantenible. El módulo de billing hace más de una cosa a la vez y en consecuencia, tiene más de una razón para cambiar. Solucionaríamos este problema haciendo un split (o separación) en distintos módulos.

Siguiendo este enfoque, una posible solución sería extraer dos módulos distintos para hacer los reportes. One desde la perspectiva del cliente y otro desde la perspectiva de la empresa, es decir la dueña de la aplicación. Ambos módulos consumirían información desde el módulo de billing. En caso de que haya requerimientos nuevos como pueden ser el agregado de columnas a los reportes o modificación de campos no requeriría ningún cambio en otros módulos.


Clases

Las clases son usualmente otro lugar común en donde SRP se viola. Si en tu proyecto encuentras clases que se denominan Manager, Engine o quizás Service seguro será el primer lugar para revisar. Veamos un ejemplo:

Según el nombre que le hemos definido, esta clase maneja todas las cosas relacionadas con Billing. Es fácil ver aquí que esta clase puede cambiar por varios motivos (cambios en los reportes, algo que pueda cambiar en el proceso de pagos y así sucesivamente. Así como lo hicimos para modificar nuestros módulos, haciendo una división de esta clase en muchas otras solucionaría el problema. Tendríamos más clases en nuestro proyecto pero éstas harían sólo una cosa y tendrían una única razón para cambiar. No tengan miedo o se sientan preocupados por la cantidad de clases en sus proyectos si esto les trae claridad a su código.


Métodos

Un momento, pensaste que habíamos terminado?

Supongamos que has logrado aplicar exitosamente SRP a tus clases separando las existentes en más clases. Es momento de revisar nuestros métodos, puede que tengamos más trabajo que hacer allí. Manos a la obra!

Sin importar si estamos utilizando un patrón MVP o MVVM, un posible Interactor (también llamado Action o Use Case) extraído de la clase BillingManager podría ser PayBilling.

El método público utilizado en esta clase tiene algún parámetro que nos da una pista de que SRP no se está respetando. Sí, así es. Es el boolean.

Este tipo de parámetro nos está forzando a tener una sentencia en la cual decidir por qué camino de los dos posibles debemos ir. Es decir, debemos pagar la última factura o bien, pagar la factura con según el número de la misma indicado en el segundo parámetro. ¿Cuántas acciones realiza este método a la vez? Sí, otra vez acertaron… dos.

Un mejor diseño sería dividiendo este método en dos para evitar romper con SRP. Aunque, antes de eso, necesitamos hacer otro pequeño cambio a nuestra clase antes de ocuparnos de este método. Cuando el problema radica en el método público de nuestro Interactor es un indicador de que es el interactor en sí mismo quién tiene más de una responsabilidad y debe ser dividido. Si creamos la clase PayLastBilling y movemos todo el código relacionado con esa acción habremos cumplido nuestro objetivo.

Ahora que hemos dividido nuestra clase, podemos focalizarnos en el método público. Dividiendo el método nuevamente alcanzaríamos nuestro objetivo final. Tendremos dos nuevos métodos privados que tendrían un único motivo de cambio cada uno.


Conclusión

Estamos de acuerdo que no importa qué tan compleja sea nuestra aplicación Android, siempre podremos mejorar nuestros diseños aplicando los principios SOLID, en este caso el principio de Single Responsibility en distintos niveles, como sean métodos, clases, módulos, etc. Siempre es importante, incluso si nuestros sistemas se interconectan con otros, el tener responsabilidades bien definidas.

Próximamente estaré subiendo la próxima parte de la serie que tocará ni más ni menos que el Principio de Open-Close. Si han llegado a este post y le interesa saber el contexto de este principio, pueden buscar el primer artículo de la serie con un resúmen sobre SOLID y las motivaciones de respetar estos principios.

Sigamos conectados 😄
Happy Coding! ✍️

0