El Principio de Responsabilidad Única

El Principio de Responsabilidad Única es probablemente el más confuso de todos los 5 principios S.O.L.I.D. Me tomó bastante tiempo entender por qué es importante y dónde aplicarlo. La redacción ha cambiado a través de los años, pero en la mayoría de lugares encontrarás una variante de la siguiente idea:

Un módulo debe tener una, y solo una razón para cambiar.

Es un concepto simple, pero la palabra ‘razón’ puede ser algo confusa. ¿Arreglar un bug cuenta como una razón? ¿Qué pasa con refactorizar el código para hacerlo más mantenible? Para obtener un mejor entendimiento de qué trata este principio, deberíamos pensar en el actor central del desarrollo de software: las personas.

Todo el software que creas está de alguna manera u otra hecho para servir las necesidades de una persona o un grupo de individuos. Cuando el software cambia, usualmente es para satisfacer los requerimientos cambiantes de estos actores. Con eso en mente, podemos reformular el principio como:

Un módulo debe ser responsable por uno, y solo un actor

Aquí, la palabra módulo se usa en el sentido abstracto. En la mayoría de lenguajes de programación populares, puedes reemplazar módulo con clase.

Ok, entiendo, ¿pero qué pasa si no sigo este principio?

La vas a pasar mal

Es mucho más fácil entender los efectos de violar este principio con un ejemplo. Supón que tenemos una clase Warehouse:

    class Warehouse
        def calculate_total_assets
            #...
            inventory_information = get_tabulated_inventory_info()
            #...
        end

        def need_additional_stock?
            #...
            inventory_information = get_tabulated_inventory_info()
            #...
        end


        def save_inventory_records
            #...
        end

        private
        def get_tabulated_inventory_info
            #...
        end
    end

Esta clase tiene los siguientes métodos:

  • calculate_total_assets calcula cuánto dinero vale nuestro stock. Es especificado y usado por el departamento de finanzas
  • need_additional_stock? analiza el inventario actual y decide si debería ordenarse nuevo stock. Usado y especificado por el gerente de bodega
  • save_inventory_records almacena la información de stock en una base de datos u otra forma de almacén de datos, especificado y usado por el departamento de ingeniería

Los cambios para esta clase pueden venir de cualquiera de los 3 actores mencionados arriba (en negrilla), ellos son las 3 razones para que cambie. ¿Por qué el diseño actual es un problema?

Bueno, supón que necesitamos modificar la forma en que se obtiene información de get_tabulated_inventory_info debido a una solicitud de finanzas. Realizamos el cambio, y el departamento de finanzas está muy contento con el nuevo comportamiento del sistema, pero en el proceso, también rompimos need_additional_stock?. La gerencia de bodega no se va a enterar de este problema hasta que descubra que el proceso para ordenar de nuevo stock está totalmente patas arriba.

El problema con violar este principio es que satisfacer cambios de diferentes fuentes en el mismo módulo/clase rutinariamente causa problemas inesperados. Si la razón para que un módulo cambie está concentrada en un solo actor, las modificaciones tienden a ser más directas y predecibles.

Veo por qué es un problema, ¿pero cómo puedo resolverlo?

La solución usual es dividir el módulo en tantos módulos como fuentes de responsabilidad.

La clase Warehouse tiene 3 fuentes de responsabilidad, así que podemos dividirla en 3 clases y mover la información de inventario a su propia estructura de datos. Después de eso, podemos crear una fachada cuya única responsabilidad será delegar los mensajes a las clases correctas:

srp

Las personas usualmente se preocupan por este enfoque por 2 razones, pero usualmente no son un problema.

La primera es duplicación. Si lo piensas, tanto AssetCalculator como StockManager necesitarán implementar sus propias versiones de get_tabulated_inventory_info. Como la forma en que manejan información es diferente (no necesitan exactamente la misma información), ambos métodos eventualmente evolucionarán en direcciones diferentes. Incluso si ambas clases tienen una copia exacta de get_tabulated_inventory_info, son conceptualmente diferentes y pertenecen a dos métodos separados.

La segunda preocupación es sobre el tamaño de las nuevas clases. Tienen miedo de terminar con un montón de clases muy pequeñas, de un solo método. Esto raramente sucede en la vida real, ya que incluso operaciones simples como calculate_total_assets necesitan varias funciones privadas de apoyo para hacer su trabajo. Terminarás con clases más limpias, más cohesivas con interfaces públicas mejor definidas.

¿No se trata de ‘hacer una cosa y solo una cosa’?

No, ese es un principio diferente, usado cuando estás refactorizando tus funciones en fragmentos más pequeños:

Las funciones deben hacer una, y solo una cosa, y hacerla bien.

Debido al nombre (Principio de Responsabilidad Única), usualmente se confunde con este otro principio (muy importante), pero significan cosas diferentes y se aplican en diferentes niveles. SRP se trata de módulos y cómo satisfacen los cambios que vienen de los usuarios, mientras que este otro principio se trata de funciones.

Lo que necesito recordar sobre SRP

La diferencia entre el SRP y los otros principios S.O.L.I.D es el papel que juega el contexto alrededor del código. Mientras que los otros principios tratan con las propiedades dentro de la solución, el SRP lidia con la relación entre la estructura del código y los actores a los que tu programa sirve.

El factor que moldea este principio es el flujo de cambios: desde las personas que establecen los requerimientos hasta los módulos que los sirven. En última instancia, el principio se trata de personas.

Qué hacer a continuación:

  • Comparte este artículo con amigos y colegas. Gracias por ayudarme a llegar a personas que podrían encontrar útil esta información.
  • Lee el artículo del Tío Bob sobre el SRP.
  • Hay más información sobre el SRP en el capítulo 7 de Clean Architecture.

Juan Luis Orozco Villalobos

¡Hola! Soy Juan, ingeniero de software y consultor en Budapest. Me especializo en computación en la nube e IA, y me encanta ayudar a otros a aprender sobre tecnología e ingeniería