Modular design concepts: Coupling
What is coupling in modular design? Let's learn about their various types and fields of application.
Table of contents
What is coupling?
- the degree of interdependence between modules
- the strength of relationships between modules,
- low coupling is thought to be a sign of a well-structured system, which is easy to enhance with new functionalities and to adjust to changing requirements and client needs.
What we know as coupling was invented by Larry Constantine in the late 1960s.
We can distinguish 6 basic coupling types. Depending on the programming environment, there can be more of them, but let’s focus on the basic ones for now.
Content coupling is a deep connection between module A and module B.
In this type of coupling, module A can modify the data of module B, and the other way round. Module A also uses the code of module B instead of a well-defined interface. In this case, modules violate the principle of information hiding.
Content coupling is the worst form of coupling and as such should be avoided.
To illustrate it, let’s take a look at the calculator module in Elixir with a very shallow interface hiding lots of functionalities.
Another case of tight coupling but the one which can solve some issues is common coupling.
In this case, several modules access the same data.
In some cases, the common coupling is acceptable. But you have to use it consciously.
A great advantage of this coupling type is its ability to keep the state and exchange information via another module. Still a tight coupling instance, common coupling has some drawbacks. These are, inter alia:
- Unforeseen side effects
- Changes require more work to be done,
- Modules using module A may need to know about each other’s existence,
- Race conditions,
- Errors in testing,
- The state is difficult to predict.
Singleton is a fine example of common coupling.
The most popular issues with Singleton are: who can change it and in what case? How to make sure that it’s always in a valid state? Is there a race condition?
A necessary evil, in a way.
External coupling takes place when there is a dependency on an external library or system.
As such dependencies are quite often the case, you should now know why this evil is necessary.
On the other hand, we can try to reduce the occurrence of external dependencies in our systems. We can even constrain such occurrences to only one module dedicated to a given library.
External coupling can be reduced by creating layers between libraries. Facade and Repository pattern are the two most popular technical solutions for limiting or even preventing the storage of external libraries in our code.
However, eliminating external coupling is rather impossible. E.g. in Elixir, we use the Ecto library for queries and it would be hard to completely skip it.
Therefore, the external coupling should not be perceived as a major threat to our code.
Which, in fact, can be quite useful and simplify our code.
How does it work?
Module A passes control information to module B.
The control information can be a flag, function, a variable. Literally, all that can alter a function’s behavior.
This type of coupling can be catastrophic if the transfer of control information can alter the behavior of a different function, e.g. a function originally calculating a sum starts solving a different equation.
On the other hand, control coupling can greatly help us with building reusable modules. We can pass policies, libraries, etc. as adapters.
It also can become an important part of writing deep modules.
This mathematics-born concept is an example of loose coupling when modules A and B share a composite data structure S, and both modules use different parts of this structure.
The structure S is accepted by modules A and B as an argument.
Stamp coupling is used frequently in programming. One of its main drawbacks is that the code in which large structures are shared between modules can be less performant than the one transferring smaller structures.
On the other hand, computers are capable of handling even modules sharing big structures, so the above drawback is not a big deal.
… which is an example of loose, perfect coupling
In this type of coupling, modules communicate by passing elementary data. Both modules are independent of each other.
An example of modules calculating roots and powers passing data between each other can be a great illustration of data coupling.
Loose & tight coupling. Summary
What is coupling?
Loose coupling allows to easily change or replace parts of a system.
It also makes testing much easier, as each module can be tested independently.
Although performance disadvantages may happen with loose coupling, it is still much easier to make such a system more performant, than the other way round (i.e. to turn a well-performing system into a loosely coupled system). Always focus on a clean, readable, and modular code first, then fix performance issues.
Tight coupling can be OK if we understand why, when, and how to use it.
But a tightly coupled system is always more prone to errors and harder to debug than a loosely coupled one.
At the end of the day, both coupling types have their applications in practice and as developers, we should not shy away from using them if the situation demands it.