21. Principios básicos de diseño: cohesión, acoplamiento y abstracción

21.1 Introducción

Diseñar software no consiste solamente en dividir un sistema en archivos, clases o módulos. También implica evaluar si esa división es clara, mantenible y adecuada para los cambios futuros. Para hacerlo, usamos principios de diseño.

Entre los principios más importantes se encuentran la cohesión, el acoplamiento y la abstracción. Estos conceptos ayudan a decidir si una parte del sistema tiene responsabilidades bien agrupadas, si depende demasiado de otras partes y si oculta o expone el nivel correcto de detalle.

Comprender estos principios permite detectar diseños frágiles antes de que el sistema crezca demasiado.

21.2 ¿Qué son los principios de diseño?

Los principios de diseño son criterios que orientan la organización interna del software. No son reglas mecánicas que se aplican igual en todos los casos, sino guías para tomar mejores decisiones.

Sirven para:

  • Reducir complejidad innecesaria.
  • Mejorar comprensión del código.
  • Facilitar pruebas.
  • Disminuir impacto de cambios.
  • Evitar duplicación y responsabilidades mezcladas.
  • Mejorar mantenimiento y evolución.
  • Hacer más clara la comunicación técnica del equipo.
Idea clave: los principios de diseño ayudan a que el software no solo funcione, sino que pueda seguir cambiando sin volverse inmanejable.

21.3 Cohesión

La cohesión indica qué tan relacionadas están las responsabilidades dentro de un módulo, clase, función o componente. Una parte tiene alta cohesión cuando todo lo que contiene contribuye a un propósito claro.

Ejemplos de alta cohesión:

  • Un módulo de reservas contiene lógica para crear, cancelar y consultar reservas.
  • Un componente de autenticación gestiona inicio de sesión, cierre de sesión y validación de credenciales.
  • Un servicio de notificaciones se encarga de enviar mensajes y registrar resultados de envío.

La cohesión ayuda a que cada parte del sistema sea más fácil de entender, probar y modificar.

21.4 Baja cohesión

Hay baja cohesión cuando una parte del sistema agrupa responsabilidades que no están claramente relacionadas. Esto suele ocurrir cuando se crea un módulo genérico que crece sin criterio.

Ejemplos de baja cohesión:

  • Un archivo llamado Utilidades contiene cálculos de descuentos, envío de correos, validación de usuarios y generación de reportes.
  • Una clase GestorGeneral se encarga de pedidos, clientes, pagos, stock y notificaciones.
  • Una pantalla contiene reglas de negocio, consultas a base de datos, formato visual y envío de mensajes.

La baja cohesión dificulta saber dónde hacer un cambio y aumenta el riesgo de romper comportamientos no relacionados.

21.5 Cómo mejorar la cohesión

Para mejorar la cohesión conviene revisar responsabilidades y agruparlas por propósito.

  • Nombrar módulos según una responsabilidad clara.
  • Separar reglas de negocio de detalles de presentación o infraestructura.
  • Dividir clases o funciones que hacen demasiadas cosas.
  • Agrupar operaciones que cambian por los mismos motivos.
  • Eliminar módulos genéricos que acumulan tareas no relacionadas.
  • Revisar si el nombre de una parte describe realmente todo lo que contiene.

Una señal práctica: si para describir un módulo necesitamos usar muchas veces "y también", probablemente tiene responsabilidades mezcladas.

21.6 Acoplamiento

El acoplamiento indica qué tan dependiente es una parte del sistema respecto de otra. Un acoplamiento bajo significa que los módulos pueden colaborar sin conocer demasiados detalles internos entre sí.

Ejemplo de acoplamiento razonable:

  • El módulo de pedidos solicita al módulo de pagos que procese un pago mediante una interfaz definida.
  • Pedidos no necesita conocer detalles internos del proveedor de pagos.
  • Si cambia el proveedor, el impacto se concentra en el módulo de pagos.

El objetivo no es eliminar todas las dependencias. Un sistema necesita partes que colaboren. El objetivo es evitar dependencias innecesarias, ocultas o demasiado rígidas.

21.7 Alto acoplamiento

Hay alto acoplamiento cuando una parte depende demasiado de detalles internos de otra. Esto provoca que un cambio pequeño tenga impacto en muchas partes.

Señales de alto acoplamiento:

  • Un módulo accede directamente a datos internos de otro.
  • Un cambio en una clase obliga a modificar muchas clases no relacionadas.
  • La lógica de negocio depende de detalles de base de datos o interfaz.
  • Las pruebas requieren preparar demasiadas dependencias.
  • El sistema no puede reemplazar un servicio externo sin modificar muchas partes.
  • Los módulos se llaman entre sí de forma circular.

El alto acoplamiento aumenta el costo del cambio y reduce la confianza para modificar el sistema.

21.8 Cómo reducir el acoplamiento

Algunas prácticas para reducir acoplamiento son:

  • Definir interfaces claras entre módulos.
  • Ocultar detalles internos mediante encapsulamiento.
  • Evitar que la lógica de negocio dependa de detalles técnicos innecesarios.
  • Separar responsabilidades por capas cuando aporte claridad.
  • Evitar dependencias circulares.
  • Usar contratos estables para comunicar módulos.
  • Revisar dependencias antes de agregar una nueva relación entre partes.

21.9 Cohesión y acoplamiento juntos

Un diseño sano suele buscar alta cohesión y bajo acoplamiento. Esto significa que cada parte tiene responsabilidades relacionadas y se comunica con otras partes mediante relaciones claras y controladas.

Situación Consecuencia
Alta cohesión y bajo acoplamiento Diseño más comprensible, mantenible y fácil de probar.
Alta cohesión y alto acoplamiento Cada módulo tiene propósito claro, pero depende demasiado de otros.
Baja cohesión y bajo acoplamiento Las partes no dependen tanto entre sí, pero internamente son confusas.
Baja cohesión y alto acoplamiento Diseño difícil de entender, probar, modificar y mantener.

21.10 Abstracción

La abstracción consiste en representar lo importante y ocultar detalles que no son necesarios para usar o comprender una parte del sistema. Permite trabajar con ideas de mayor nivel sin estar pendiente de todos los detalles internos.

Ejemplos:

  • Un módulo de pagos expone "procesar pago" sin mostrar todos los pasos del proveedor externo.
  • Un repositorio expone "guardar pedido" sin revelar cómo se escribe en la base de datos.
  • Una interfaz de notificaciones expone "enviar mensaje" sin distinguir inicialmente si será correo, SMS o notificación móvil.

La abstracción ayuda a reducir complejidad, pero debe usarse con criterio. Abstraer demasiado pronto puede crear estructuras innecesarias.

21.11 Buenas y malas abstracciones

Una buena abstracción simplifica el uso de algo sin esconder información necesaria. Una mala abstracción oculta diferencias importantes o agrega complejidad sin beneficio real.

Tipo Características Ejemplo
Buena abstracción Expone operaciones claras y oculta detalles cambiantes. Servicio de notificaciones con operación enviarConfirmacion.
Mala abstracción Es demasiado genérica, confusa o no representa un concepto real. Un servicio llamado ProcesadorGeneral que hace pagos, correos y reportes.

Una abstracción debe surgir de una necesidad real de comprensión, cambio o reutilización.

21.12 Encapsulamiento

El encapsulamiento consiste en proteger los detalles internos de una parte del sistema y exponer solo una interfaz controlada. Está muy relacionado con la abstracción y el acoplamiento.

Beneficios del encapsulamiento:

  • Reduce dependencias con detalles internos.
  • Permite cambiar implementación sin afectar a quien usa el módulo.
  • Ayuda a mantener invariantes y reglas internas.
  • Evita usos incorrectos de datos internos.
  • Facilita pruebas y mantenimiento.

Cuando todo es accesible desde cualquier parte, el sistema se vuelve frágil porque cualquier módulo puede modificar datos o estados sin respetar reglas.

21.13 Separación de responsabilidades

La separación de responsabilidades indica que cada parte del sistema debería encargarse de un conjunto coherente de tareas. Está directamente relacionada con la cohesión.

Ejemplo:

  • La interfaz muestra datos y captura acciones del usuario.
  • La lógica de aplicación coordina casos de uso.
  • El dominio contiene reglas del negocio.
  • La infraestructura resuelve detalles técnicos como base de datos, archivos o servicios externos.

Separar responsabilidades permite cambiar una parte sin afectar innecesariamente a las demás. Por ejemplo, cambiar la base de datos no debería obligar a modificar reglas de negocio.

21.14 Principio de menor conocimiento

Un principio relacionado indica que una parte del sistema debería conocer solo lo necesario para cumplir su tarea. Cuanto más conoce sobre detalles de otros módulos, más acoplada queda.

Ejemplo de exceso de conocimiento:

El módulo de pedidos conoce cómo se conecta el proveedor de pagos, cómo se arma el correo de confirmación y cómo se actualiza directamente la tabla de stock.

Una alternativa más limpia:

  • Pedidos solicita procesar pago al módulo de pagos.
  • Pedidos solicita actualizar stock al módulo de inventario.
  • Pedidos solicita enviar confirmación al módulo de notificaciones.

Así cada módulo conoce contratos, no detalles internos.

21.15 Ejemplo: rediseñar una funcionalidad de compra

Supongamos una función llamada confirmarCompra que hace todo: valida usuario, calcula descuentos, procesa pago, actualiza stock, genera factura y envía correo.

Problemas:

  • Baja cohesión: concentra muchas responsabilidades distintas.
  • Alto acoplamiento: conoce detalles de pagos, stock, facturación y correo.
  • Difícil de probar: hay que preparar muchas dependencias.
  • Difícil de cambiar: modificar descuentos puede afectar pagos o notificaciones.

Un diseño mejor podría separar módulos:

  • Servicio de pedidos coordina la operación.
  • Servicio de descuentos calcula promociones.
  • Servicio de pagos procesa el cobro.
  • Servicio de inventario actualiza stock.
  • Servicio de facturación genera comprobantes.
  • Servicio de notificaciones informa al cliente.

El diseño resultante no es necesariamente más corto, pero es más comprensible y mantenible.

21.16 Equilibrio: no sobrediseñar

Aplicar principios de diseño no significa crear muchas capas, interfaces y abstracciones para cualquier problema. El exceso de diseño también puede hacer el sistema difícil de entender.

Señales de sobrediseño:

  • Hay muchas abstracciones para una funcionalidad muy simple.
  • El equipo no puede explicar por qué existe una interfaz.
  • Se crearon módulos para cambios que quizá nunca ocurran.
  • La estructura técnica es más difícil que el problema que resuelve.
  • Agregar una función pequeña requiere tocar demasiadas capas sin beneficio claro.

El buen diseño busca equilibrio: suficiente estructura para manejar complejidad, pero no tanta como para crear complejidad artificial.

21.17 Errores comunes

Al aplicar principios de diseño suelen aparecer errores como:

  • Confundir dividir en muchos archivos con buen diseño.
  • Crear módulos con baja cohesión y nombres genéricos.
  • Permitir que todos los módulos conozcan detalles de todos.
  • Duplicar reglas de negocio por no definir una responsabilidad clara.
  • Usar abstracciones que no simplifican nada.
  • Aplicar patrones o capas por costumbre, sin necesidad real.
  • No revisar el diseño cuando aparecen cambios repetidos.
  • Ignorar pruebas como señal de diseño: si algo es muy difícil de probar, quizá está mal separado.

21.18 Buenas prácticas

Algunas prácticas recomendables son:

  • Buscar alta cohesión: cada parte debe tener un propósito claro.
  • Reducir acoplamiento innecesario entre módulos.
  • Crear abstracciones cuando simplifican uso, cambio o comprensión.
  • Encapsular detalles internos detrás de interfaces claras.
  • Separar reglas de negocio de detalles técnicos y de presentación.
  • Revisar nombres: si el nombre es confuso, quizá la responsabilidad también lo es.
  • Usar pruebas para validar si el diseño permite aislar comportamientos.
  • Refactorizar cuando el diseño deja de acompañar al sistema.

21.19 Qué debes recordar de este tema

  • La cohesión mide qué tan relacionadas están las responsabilidades dentro de una parte del sistema.
  • Un diseño con alta cohesión es más fácil de entender y mantener.
  • El acoplamiento mide cuánto depende una parte de los detalles de otra.
  • Un diseño con bajo acoplamiento reduce el impacto de los cambios.
  • La abstracción permite trabajar con conceptos importantes ocultando detalles innecesarios.
  • El encapsulamiento protege detalles internos y expone interfaces controladas.
  • El buen diseño requiere equilibrio: ni desorden ni complejidad artificial.

21.20 Conclusión

Cohesión, acoplamiento y abstracción son principios fundamentales para evaluar y mejorar el diseño de software. Ayudan a que las responsabilidades estén bien agrupadas, las dependencias sean controladas y los detalles internos no se filtren innecesariamente.

Para quien comienza, la idea principal es esta: un diseño de calidad agrupa lo que debe cambiar junto, separa lo que tiene motivos distintos para cambiar y expone solo lo necesario para colaborar.

En el próximo tema veremos una introducción a la arquitectura de software, donde estas ideas se aplican a decisiones estructurales de mayor escala.