5. Principio de Responsabilidad Única (Single Responsibility Principle - SRP)

El Principio de Responsabilidad Única (SRP, Single Responsibility Principle) establece que una clase, módulo o componente debe tener un solo motivo para cambiar. En otras palabras, cada unidad de código debe concentrarse en resolver un problema específico del dominio.

SRP es el primer principio del acrónimo SOLID y funciona como una brújula para el diseño orientado a objetos en proyectos reales. Aplicarlo minimiza cambios colaterales, hace que el código sea más comprensible y prepara el terreno para los demás principios.

5.1 Interpretaciones modernas del SRP

Una interpretación común dice que la responsabilidad equivale a una razón de cambio. Otra agrega que cada clase debe representar un actor del sistema: un rol dentro de la organización o un proceso del negocio. Ambas ideas se complementan y permiten evaluar si la clase forma parte de una única historia de usuario.

5.2 Señales que indican violaciones al SRP

  • Métodos excesivamente largos: el método hace varias tareas sin un hilo conductor claro.
  • Campos heterogéneos: la clase mantiene datos que no están relacionados entre sí.
  • Dependencias inconsistentes: el componente colabora con servicios que no comparten el mismo objetivo.
  • Frecuencia de cambios: la clase se modifica cada vez que se toca un área distinta del sistema.

5.3 Beneficios de aplicar SRP

Al dividir responsabilidades, el código gana claridad, la cobertura de pruebas crece y los equipos pueden estimar tareas con mayor precisión. Además, la arquitectura se vuelve más flexible para aplicar otros principios SOLID como OCP o DIP.

5.4 Ejemplo de violación al SRP en Java

Veamos una clase que gestiona usuarios, registra auditorías y envía correos en un mismo lugar. Cada responsabilidad pertenece a un área distinta del sistema.

class UsuarioService {
    void crearUsuario(Usuario usuario) {
        validar(usuario);
        guardar(usuario);
        registrarAuditoria(usuario);
        enviarMailBienvenida(usuario);
    }

    private void validar(Usuario usuario) { /* ... */ }
    private void guardar(Usuario usuario) { /* ... */ }
    private void registrarAuditoria(Usuario usuario) { /* ... */ }
    private void enviarMailBienvenida(Usuario usuario) { /* ... */ }
}

Esta clase viola SRP porque mezcla validación, persistencia, auditoría y comunicación con clientes. Cualquier cambio en uno de esos procesos obliga a modificar el servicio principal.

5.5 Refactorización guiada por SRP

El primer paso consiste en extraer cada responsabilidad a un colaborador especializado. De esta manera, el servicio central coordina la creación de usuarios sin implementar los detalles concretos.

interface ValidadorUsuarios {
    void validar(Usuario usuario);
}

interface RepositorioUsuarios {
    void guardar(Usuario usuario);
}

interface AuditorUsuarios {
    void registrarAlta(Usuario usuario);
}

interface NotificadorUsuarios {
    void enviarBienvenida(Usuario usuario);
}

class UsuarioService {
    private final ValidadorUsuarios validador;
    private final RepositorioUsuarios repositorio;
    private final AuditorUsuarios auditor;
    private final NotificadorUsuarios notificador;

    UsuarioService(ValidadorUsuarios validador,
                  RepositorioUsuarios repositorio,
                  AuditorUsuarios auditor,
                  NotificadorUsuarios notificador) {
        this.validador = validador;
        this.repositorio = repositorio;
        this.auditor = auditor;
        this.notificador = notificador;
    }

    void crearUsuario(Usuario usuario) {
        validador.validar(usuario);
        repositorio.guardar(usuario);
        auditor.registrarAlta(usuario);
        notificador.enviarBienvenida(usuario);
    }
}

La clase refactorizada cumple con SRP porque solo se ocupa de orquestar el proceso. Si necesitamos introducir un nuevo canal de comunicación, podemos hacerlo agregando una implementación de NotificadorUsuarios sin modificar UsuarioService.

5.6 Estrategias para detectar responsabilidades ocultas

  • Revisar nombres: si el nombre de la clase contiene “y” (&) o múltiples sustantivos, quizá agrupa más de una tarea.
  • Analizar la cohesión: identificar si los métodos comparten los mismos atributos o si cada uno manipula datos distintos.
  • Identificar cambios recurrentes: un historial de commits que modifica la misma clase por razones diferentes suele indicar violaciones al SRP.
  • Utilizar diagramas de secuencia: visualizar el flujo ayuda a separar pasos que pertenecen a actores diferentes.

5.7 SRP y pruebas automatizadas

Una clase con responsabilidad única facilita la creación de pruebas específicas. En el ejemplo de arriba, podemos simular cada dependencia con mocks y verificar que el servicio orquesta las colaboraciones en el orden correcto. Esto reduce el tiempo de diagnóstico ante fallos.

5.8 SRP en arquitecturas mayores

El principio no se limita a clases. También aplica a módulos, microservicios y capas completas. Por ejemplo, un microservicio debería tener un único propósito dentro del dominio. Si maneja reporting, facturación y gestión de usuarios, probablemente está violando SRP a gran escala.

Adoptar SRP desde las primeras iteraciones del proyecto crea un lenguaje común dentro del equipo y allana el camino para los siguientes principios SOLID. En los capítulos próximos veremos cómo se complementa con el principio abierto/cerrado.