Cohesión y Acoplamiento

Junto con ponerle nombres a las cosas, uno de los aspectos más complicados a la hora de desarrollar software es decidir cómo asignar las distintas responsabilidades de la aplicación, en qué componentes repartirlas y cómo agruparlas. Existe mucha literatura al respecto y si queréis empezar por algunos clásicos, al menos en lo que a diseño orientado a objeto se refiere, podéis echarle un vistazo a los patrones GASP.

En este post vamos a centrarnos en dos aspectos que, aunque a primera vista pueden parecer ortogonales, en ocasiones están más ligados de lo que nos gustaría: la cohesión y el acoplamiento.

Podríamos definir la cohesión como lo estrecha que es la relación entre los componentes de algo. Si hablamos de clases, una clase tendrá una cohesión alta si sus métodos están relacionados entre sí, tienen una “temática” común, trabajan con tipos similares, etc. Si pasamos a componentes de mayor tamaño, como paquetes o librerías, tendríamos una cohesión alta cuando las clases que lo forman están muy relacionadas entre sí, con un objetivo claro y focalizado.

El acoplamiento es la manera que se relacionan varios componentes entre ellos. Si existen muchas relaciones entre los componentes, con muchas dependencias entre ellos, tendremos un grado de acoplamiento alto. Si los componentes son independientes unos de otros, el acoplamiento será bajo. Al igual que con la cohesión, podemos ver el acoplamiento a distintos niveles y existe acoplamiento entre los métodos de una misma clase (o las funciones de un módulo), entre distintas clases o entre distintos paquetes. Además, existen varios tipos de acoplamiento, desde acoplamiento a través de datos comunes, acoplamiento temporal (es necesario utilizar los componentes en un orden concreto), etc.

Como decía al principio del post, a priori ambas dimensiones son independientes, pero en la práctica no siempre es así. Algo parecido veíamos al hablar sobre la Ley de Demeter; Don’t Ask y God Object: si queremos aumentar la cohesión podemos acabar creando un alto acoplamiento entre distintas funcionalidades del sistema.

Podemos mostrar ambas dimensiones en un gráfico:

cohesión y acoplamiento

Está claro que lo ideal es situarnos en el cuadrante de alta cohesión y bajo acoplamiento (eso es Bueno™), y lo peor que nos puede pasar es acabar en la situación opuesta, con baja cohesión y alto acoplamiento (eso es Malo™), pero si tenemos que perder uno u otro, ¿qué sacrificios estamos haciendo? ¿Qué es lo que nos perdemos y lo que ganamos en cada caso?

Favorecer la alta cohesión

Supongamos que nos movemos hacia el cuadrante de la alta cohesión a costa de aumentar también el acoplamiento.

Un código muy cohesionado tiende a ser más autocontenido y, precisamente por eso, suele ser más fácil de entender como un todo porque tiene menos dependencias externas y un API más definida. Podemos tratarlo como una caja negra que encapsula de forma férrea toda la lógica e información que contiene y eso hace que sea más sencillo olvidarse de su implementación interna.

Además, si aumentamos la cohesión podemos tener menos componentes, lo que nos ayuda a descubrir las funcionalidades del sistema y a tener más claro dónde asignar cada responsabilidad.

En un componente con una alta cohesión podemos llegar a mezclar responsabilidades que, en algunos casos, podríamos ver como independientes. Por ejemplo, en una aplicación podríamos tener un componente que encapsula a la vez DOM, CSS y Javascript. Esto facilita su uso porque sólo hay que considerar un componente a la hora de utilizarlo, pero limita la flexibilidad porque estamos acoplando responsabilidades que, tal vez, deberíamos separar (el aspecto con el CSS y la funcionalidad con el Javascript).

El riesgo claro de esto es acabar construyendo un supercomponente que hace mil cosas en aras de la cohesión. Lo curioso es que en ese escenario, la búsqueda de la cohesión nos lleva justamente a todo lo contrario, acabar con un componente poco cohesionado en el que existen operaciones que realizan tareas que no tienen nada que ven entre ellas.

También hay que tener en cuenta la pérdida de libertad que mencionábamos antes: al acoplar más la funcionalidad, será más complicado reemplazar partes de la misma para adecuarla a nuevos escenarios.

Favorecer el bajo acoplamiento

Si preferimos situarnos en el cuadrante del bajo acoplamiento a costa de perder cohesión, tendremos un escenario diferente.

Al favorecer el bajo acoplamiento lo normal es que tengamos componentes más pequeños, con responsabilidad más definida y, por tanto más fáciles de entender por separado.

Estos componentes serán, en general, más reutilizables, precisamente porque al ser independientes unos de otros e incluir menos funcionalidades habrá más escenarios en los que tengan cabida.

Ambos factores ayudan a tener un sistema más flexible, en el que podemos reconfigurar las relaciones entre las funcionalidades que ya tenemos implementadas para añadir otras nuevas.

Un riesgo asociado a un diseño en el que todos los componentes están muy desacoplados unos de otros es que al tender a ser componentes más pequeños y reutilizables, se acaban reutilizando en muchos contextos, por lo que aquello que inicialmente era una ventaja porque nos permitía desacoplar aspectos dentro de una funcionalidad, acaba por acoplar unas funcionalidades con otras a través del uso de componentes comunes.

Además, comprender la estructura global de un sistema en el que todo está muy desacoplado puede ser más difícil porque necesitamos comprender no sólo los componentes individuales sino las relaciones entre ellos, y éstas suelen ser menos explícitas y directas para poder reducir el acoplamiento.

Distintos niveles, distintas prioridades

Al definir la cohesión y el acoplamiento veíamos que ambos conceptos pueden aplicarse a distintos niveles y, en mi experiencia, dependiendo del nivel en el que nos estemos moviendo, merece la pena preocuparse más de una cosa u otra.

Dentro de una funcionalidad concreta, prefiero primar la alta cohesión.

Si tengo un componente para mostrar en pantalla resultados deportivos, prefiero que el componente sea lo más autocontenido y cohesivo posible para facilitar su reutilización, aunque ello implique un mayor acoplamiento entre sus distintas facetas y tenga en un mismo “paquete” el código CSS, la obtención del datos del servidor y el HTML necesario para renderizarlo.

Igualmente, si tengo un componente (una clase o un grupo de clases) para representar una factura, prefiero primar la cohesión aunque eso me haga perder flexibilidad, por ejemplo a la hora de cambiar la forma en que se calculan los impuestos. A veces esto se manifiesta en el uso de clases privadas, en no utilizar inyección de dependencias o en minimizar el API externa.

Cuando se trata de coordinar las distintas funcionalidades de una aplicación, le doy más importancia a conseguir un bajo acoplamiento.

Intento evitar dependencias rígida entre las distintas funcionalidades, introduciendo niveles de indirección entre ellas mediante interfaces, estructuras de datos básicas para el intercambio de datos y, en general, cualquier cosa que me permita minimizar el conocimiento de unas con respecto a las otras.

Si tengo un módulo de facturación y otro de gestión de clientes, probablemente intentaré que el módulo de facturación no tenga dependencias directas sobre el módulo de clientes y limitaré el acceso al mismo al mínimo, tratando de no compartir estructuras de datos complejas (clases) para facilitar la evolución por separado de cada módulo. Algo parecido a lo que comentaba al hablar sobre diseño de modelos.

Conclusiones

No hay (o la menos yo no la tengo) una respuesta clara a qué debemos primar en caso de que no podamos conseguir una alta cohesión y un bajo acoplamiento, en función de cada proyecto (y cada equipo de desarrollo) puede ser más importante dar prioridad a una u otra cosa.

Como siempre, lo importante es conocer las implicaciones de las decisiones que tomamos y ser capaces de valorar lo que estamos consiguiendo y lo que estamos perdiendo por el camino.


Un comentario en “Cohesión y Acoplamiento

  1. Juan Carlos Villalobos Cardona dijo:

    Muy buen artículo, una practica forma de ver estos dos aspectos. Felicitaciones.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>