8. Vinculación con los principios SOLID y patrones de diseño asociados

Los principios SOLID ofrecieron un vocabulario concreto para hablar de acoplamiento y cohesión. Cada principio ayuda a controlar estas fuerzas y, junto con patrones de diseño, sirven como guía para construir software flexible, preparado para el cambio sin sacrificar claridad.

8.1 El puente entre SOLID y el equilibrio arquitectónico

Aplicar SOLID no es un fin en sí mismo: es el medio para mantener el equilibrio entre dependencias controladas y responsabilidades enfocadas. Cuando un proyecto adopta estos principios de forma consciente, las decisiones de diseño se vuelven más explícitas porque existen criterios objetivos para aceptar o rechazar cambios en la estructura.

8.2 Principio de Responsabilidad Única (SRP) y cohesión

El SRP establece que una clase debe tener un solo motivo para cambiar. Su objetivo esencial es elevar la cohesión, evitando que una unidad concentre tareas ajenas. En Java esto suele implicar segmentar entidades de dominio, servicios de aplicación y adaptadores de infraestructura:

class GeneradorResumenDiario {
    private final RepositorioPedidos repositorioPedidos;
    private final ServicioReporte servicioReporte;

    GeneradorResumenDiario(RepositorioPedidos repositorioPedidos,
                           ServicioReporte servicioReporte) {
        this.repositorioPedidos = repositorioPedidos;
        this.servicioReporte = servicioReporte;
    }

    void generar(LocalDate fecha) {
        List<Pedido> pedidos = repositorioPedidos.buscarPorFecha(fecha);
        servicioReporte.crearResumen(pedidos);
    }
}

El generador coordina actividades sin mezclar cómo se obtienen los pedidos o se produce el reporte. Cada colaborador mantiene su propia cohesión y motivo de cambio.

8.3 Principio Abierto/Cerrado (OCP) y la extensibilidad sin acoplar

El OCP propone que los módulos estén abiertos a la extensión pero cerrados a la modificación. Al implementar nuevas variantes mediante herencia controlada o composición, se reduce la necesidad de tocar código existente, por lo que las dependencias se mantienen estables. Patrones como Estrategia, Decorador o incluso clases de configuración modular permiten cumplir este principio.

8.4 Principio de Inversión de Dependencia (DIP) como defensa ante el acoplamiento

El DIP indica que las clases de alto nivel no deben depender de detalles de bajo nivel, sino de abstracciones. Esto evita que cambios en la infraestructura se propaguen al dominio. Es común apoyarse en inyección de dependencias o en interfaces implementadas por adaptadores específicos.

8.5 El patrón Fachada como moderador del acoplamiento

El patrón Fachada encapsula subsistemas complejos tras una interfaz simple. Su uso limita el acoplamiento al exponer un punto único de entrada y proteger a los consumidores de detalles internos que probablemente cambiarán.

8.6 La responsabilidad única para aumentar cohesión

Patrones como Comando, Servicio de Dominio o Adaptador ponen el foco en narrar una historia clara. Cada objeto describe una sola intención, lo que facilita comprender su comportamiento y refactorizarlo. Esta idea se complementa con el SRP y permite que cada colaborador se pruebe de manera aislada.

8.7 Ejemplo integrado: SRP, OCP y DIP en acción

El siguiente ejemplo combina los principios y patrones mencionados. Define una fachada para orquestar el proceso de facturación y delega el cálculo de impuestos en estrategias extensibles:

interface EstrategiaImpuestos {
    BigDecimal calcular(Pedido pedido);
}

class ImpuestoGeneral implements EstrategiaImpuestos {
    public BigDecimal calcular(Pedido pedido) {
        return pedido.subtotal().multiply(new BigDecimal("0.21"));
    }
}

class ImpuestoReducido implements EstrategiaImpuestos {
    public BigDecimal calcular(Pedido pedido) {
        return pedido.subtotal().multiply(new BigDecimal("0.10"));
    }
}

class FachadaFacturacion {
    private final RepositorioPedidos repositorioPedidos;
    private final RepositorioFacturas repositorioFacturas;
    private EstrategiaImpuestos estrategiaImpuestos;

    FachadaFacturacion(RepositorioPedidos repositorioPedidos,
                       RepositorioFacturas repositorioFacturas,
                       EstrategiaImpuestos estrategiaImpuestos) {
        this.repositorioPedidos = repositorioPedidos;
        this.repositorioFacturas = repositorioFacturas;
        this.estrategiaImpuestos = estrategiaImpuestos;
    }

    Factura emitir(Long idPedido) {
        Pedido pedido = repositorioPedidos.buscar(idPedido);
        BigDecimal impuestos = estrategiaImpuestos.calcular(pedido);
        Factura factura = Factura.crear(pedido, impuestos);
        repositorioFacturas.guardar(factura);
        return factura;
    }

    void cambiarEstrategia(EstrategiaImpuestos nuevaEstrategia) {
        this.estrategiaImpuestos = nuevaEstrategia;
    }
}

La fachada concentra el flujo de facturación (cohesión alta), protege al cliente de detalles de repositorios (acoplamiento reducido) y permite extender el cálculo de impuestos mediante nuevas estrategias sin modificar el código existente (OCP). Además, depende de la interfaz EstrategiaImpuestos (DIP), por lo que cualquier cambio en el código tributario se encapsula tras una abstracción.

8.8 Estrategias para introducir SOLID en proyectos existentes

Como en toda refactorización, conviene avanzar de forma incremental:

  • Identificar clases con baja cohesión y mapear qué principios están siendo incumplidos.
  • Extraer interfaces y mover responsabilidades gradualmente, acompañando con pruebas automatizadas para evitar regresiones.
  • Documentar las zonas donde el acoplamiento es inevitable (por ejemplo, integraciones externas) y encapsularlas tras fachadas o adaptadores.
  • Revisar la configuración de inyección para asegurarse de que las dependencias se resuelvan según el contrato y no mediante detalles concretos.

8.9 Lista de comprobación para SOLID y patrones

Al evaluar una iteración, utilice las siguientes preguntas como guía:

  • ¿Cada clase tiene un único motivo de cambio claramente identificable?
  • ¿Las variantes del dominio se agregan extendiendo código y no modificando el existente?
  • ¿Las dependencias de alto nivel están expresadas mediante interfaces o abstracciones?
  • ¿Los subsistemas complejos están encapsulados tras fachadas u otros patrones que moderen el acoplamiento?

Alinearse con SOLID y emplear patrones apropiados permite dominar el equilibrio entre acoplamiento y cohesión. El resultado es una arquitectura preparada para evolucionar, con componentes comprensibles y conexiones controladas.