In the past few posts, we have considered that programs can be composed of smaller parts. The ability to reduce a program to a collection of smaller, connected algorithms is known as modularity.
When we consider a set of modules (smaller algorithms which are connected to form a program), we can consider a measure of how tightly they are bound together. This measure is called coupling. The more tightly coupled two modules are, the more they rely on each other, and the less independent they are.
We consider a few loose classes of coupling:
- Independent – These two modules don’t touch each other at all. The more independent a function, the more likely you can reuse it later.
- Loosely coupled – One module relies on the results of the other. The more loosely coupled modules can handle any possible results, while more tightly coupled modules require only certain results. These are still relatively easy to reuse, though you often need a set of modules to make reuse viable.
- Common (Global) coupling – Two modules rely on the same global data element. They are otherwise independent, but changing the global data element changes the results of these modules.
- Content Coupling – One module relies on the inner workings of another module. This is about as tight a coupling as you can get, because changing one module drastically changes the other.
- Control coupling – Two modules share a set of state data, which we call flags. These flags control how the logic of a program will flow. Control coupling is essential in almost every synchronized communication, such as networking or gaming.
Cohesion is inversely related to coupling. That is, the more tightly two modules are coupled, the higher the cohesion between those two modules.
There are a few types of cohesion which we commonly consider:
- Functional cohesion – This is a module with only one function/process. This is the simplest (and therefore, easiest for programmers to work with) form of cohesion. Functional cohesion has two basic rules:
- Functions do only one thing – Everything within the function serves to accomplish that one thing, with as little side-tracking as possible
- Functions only do that one thing in one place – Functions should operate only in one scope. That is, only modify global variables or local variables or function variables. This reduces the amount of places where bugs can be.
- Sequential cohesion – This is a module with several tasks which are closely related to one another. Usually, these tasks flow together in one sequence (hence the name), with one function passing its results on to the next.
- Communication Cohesion – This is a set of modules whose functions work on the same set of data. For example, a set of modules which read input from the keyboard, copy the input to a buffer, encrypt the buffer, then send the buffer to another user has strong communication cohesion.
- Procedural Cohesion – This is a set of largely unrelated modules whose functions are linked by a flow of control (as opposed to a sequence). This introduces complexity to the program, as sequences are determined by the control flow – the set of decisions from all the branches in the modules.
- Temporal cohesion – This is a set of processes which usually happen at the same time. There is almost no cohesion between these objects, and if we rely on this cohesion our solution only works on our computers.
We don’t often talk about Cohesion or Coupling as such in the software world, but the concepts are integral to everything we do. It is important for you to understand these concepts in order to produce quality, maintainable, and reusable code.