Cohesion! Wait, what?
Cohesion, normally mentioned along with coupling, is a term used to characterise a module, specifically, “how well the elements of a module (think function, class or namespace for example) go together” where “element” refers to –
a “piece” of the module, such as a statement, a segment, or a “subfunction”
Unlike coupling it’s a little harder to explain due to I think, it not being a concept representable via programming language constructs as coupling is.
As a Rich Hickey fan, I’ll also begin with some dictionary defintions -
I think the definition of cohere captures the idea better than cohesion itself. In light of this, the definition could be tweaked a little: “the degree to which elements of a module are logically consistent”. But why is this important?
Comprehension, implementation and change
Increasing the cohesion of a system and its module(s) necessarily coalesces related elements of different modules and expells unrelated elements of the same module which both increases cohesion and reduces coupling of the system. A function who’s elements and logic are soley oriented toward the functions spec will be easier to comprehend than a function whose elements and logic represent a multitude of concerns because as well as trying to comprehend the spec for the flow through the function you’re interested in you’re also having to identify and ignore any irrelevent elements which obviously requires more effort. At the same time, reduced coupling means we practically spend less effort comprehending a certain domain concept for instance, because it’s not spread over many modules.
Traditionally there are six levels of cohesion, where coincidental is the weakest and functional the higheset i.e. most desirable:
- Coincidental
- Logical
- Temporal
- Communicational
- Sequential
- Functional
Coincidental cohesion describes modules whose elements have no meaningful relationship - think those “utils” or “helper” files; or perhaps a module that consolidates duplicate code. Functional cohesion on the other hand, represents modules with elements that are each “related to the performance of a single function” (Structured Design)
Interestingly these categories aren’t exclusive and a module may exhibit several at once, but being characterised by the highest available.
Level of detail
Lastly, a less mentioned subtle quality of cohesion refers to the “level of detail” for each element. While a function may be cohesive in the sense that every element contributes to the stated spec, those elements might not all be represented at the same level of abstraction. For example
Here we’re validating user data, interacting with a database, logging to a console and sending a welcome email. All elements are necessary by reason of the function but clearly not all are at the same level of abstraction - inserting a value into a database does not require the same domain understanding, logic or reason required to evaluate what a “valid” user is. The up-and-down of detail in this example may be trivial to navigate but on a larger, more complex module it has a compounding effect.
Incidental complexity
As an aside, what we’ve identified here is referred to as essential and incidental complexity; elements that pertain to the problem domain versus those that pertain to the solution domain.
As one improvement, the lower level incidental concerns of connecting to a database could expressed as a function call to persist the data, eliding the actual details of how thats done and better expressing the intent of our design that we want to persist the data; using a SQL database is just one way we could achieve that, we could also write it to a file, cache it in RAM or use a an external service via an HTTP API. The point being by expressing the all the elements at a level close to that of the function interface we make comprehension easier and less taxing.
In the end cohesion is about designing our modules such that we maximise the coherence of the elements within, both what’s in there and how it’s in there i.e. it’s level of detail. In this way we make our system simpler and easier to reason about.