Coupling

Coupling is a measure of the degree to which a component or module depends upon other components or modules in the system. Coupling is a qualitative value, which we can get a sense of by asking: Given two components X and Y, if X changes, how much code in Y must be changed?

Minimizing coupling is desirable because it allows components and modules to be used, modified, and swapped independently from other components or modules in the system.

Describing Coupling

We usually describe coupling in terms of tight/strong/high coupling and loose/weak/low coupling.

  • Two components are loosely coupled when changes in one rarely (or never) necessitate changes in the other
  • Two components are tightly coupled when they cannot be easily separated – changing one component will require corresponding changes in the other component

We can think of coupling as being logical or physical:

  • Logical coupling refers to relationships between program abstractions
  • Physical coupling refers to relationships between files, such as source/header file inclusions or library linkage

Coupling can be actual or potential:

  • Actual coupling indicates that program elements are currently connected
  • Potential coupling means that an element is not currently connected, but its visibility would allow it to be connected
    • Most excess coupling exists as potential coupling

We can categorize coupling based on the type of connection between two components or modules:

  • Content coupling (high) – when one module uses the code of another module (violating information hiding)
  • Common coupling – when several modules have access to the same global data
  • External coupling – when two modules share an externally imposed data format, communication protocol, or device interface
  • Control coupling – when one module controls the flow of another by passing it information on what to do
  • Stamp coupling (aka data-structured coupling) – when modules share a composite data structure, and use only parts of it (e.g., passing a whole record to a function that only needs one field)
    • A modification to a field that the module does not need may mean the dependent module still has to change
  • Subclass coupling – describes the relationship between a child and its parent – the child is connected to the parent, but the parent is not connected to the child
  • Temporal coupling – when two actions are bundled together into one module because they occur at the same time
  • Data coupling (low) – when modules share data through parameters; each datum is an elementary piece, and these are the only data shared

Consequences of Tight Coupling

Tight coupling has many observable consequences:

  • Individual modules are more difficult to reuse, since they have dependencies on other modules in the existing system
  • Individual modules are more difficult to test, since the modules cannot be used in isolation
  • It makes the program more difficult to reason about, since changes in one module can impact multiple modules within a system (potentially in unexpected ways)
  • Changing requirements, design decisions, and hardware components will typically trigger large scale changes in order to accommodate them

Over time, tight coupling reduces the maintainability and flexibility of the system.

Most developers realize that excess coupling is harmful but they don’t resist it aggressively enough. Believe me: if you don’t manage coupling, coupling will manage you.
— Jerry Fitzpatrick, Timeless Laws of Software Development

Benefits of Loose Coupling

Loose coupling is desirable because it allows modules and components to be developed, used, and modified independently from one another. Loosely coupled components are much easier to test, because they can be isolated from other components in the system. They can be more easily reused for the same reasons.

Loosely coupled components can also be easily replaced with alternate implementations that provide the same functionality. This makes our systems more flexible and modifiable. This is especially useful for embedded systems, as loosely coupled hardware modules can be easily swapped, enabling us to quickly add support for new board designs, sensors, and peripheral devices.

Strategies for Reducing Coupling

“ Uncertainty is not a license to guess. It is a directive to decouple.”
—  Sandi Metz

There are a number of strategies we can use for reducing coupling.

Fundamentally, we can apply foundational software principles for creating modules and components that are loosely coupled. We should strive to create modules that make use of information hiding. Our modules should adhere to the single responsibility principle. Our modules should be cohesive and provide minimal interfaces. Modules and components should communicate with each other through abstract interfaces whenever possible, allowing other modules or components that satisfy the interface to be substituted as needed. This strategy leads to three decoupling tactics:

  • Keep related parts together
  • Reduce potential coupling by using visibility options
  • Create barriers to isolate or protect parts of the system

Coupling increases between two modules under the following conditions. We can try to eliminate these conditions wherever possible.

  • One module has an internal attribute/member that refers to (or is the type of) another module
  • One module calls the functions of another module
  • One module has a method that references another module (or its type) (e.g., via a return type or parameter)
  • One module is a subclass of another module
  • One module implements a specification described by another module

We can also employ specific techniques that enable loose coupling:

We can minimize potential coupling by carefully structuring our build systems to prevent undesired coupling between elements.

Considerations

Even in loosely coupled systems, changes that affect external interfaces will still require changes in other modules. The best defense against this is to create minimal interfaces.

Some people state that coupling isn’t always bad, primarily because it can allow us to create highly cohesive modules. In our view, coupling should be prioritized over cohesion, because coupling prevents us from easily replacing modules and means that we cannot make changes to a module’s implementation independently of other modules. We also must consider the fact that some piece of our code likely will need to be tightly coupled to other pieces. Our goal is to constrain tight coupling into designated locations within the program.

However, we might reasonably ask when is tight coupling worth the cost?

  • We may need to break cohesive modules into pieces for reasons such as performance or maintainability, which then necessitates tighter coupling between less-cohesive modules
  • There are conditions where we need to have every step of a process access a piece of global data or state, which introduces tight coupling among the components
  • Performance requires tight coupling
  • Sometimes the work required to create low coupling is not worth the investment in time/code (e.g., we are likely never going to need to swap or reuse this module, so we can keep it tightly coupled)
  • Cohesion and coupling are closely related concepts. Low coupling is often achieved by creating highly cohesive components with narrow interfaces.
  • Loose coupling is often achieved through the technique of information hiding.
  • Loosely coupled code is much easier to modify, maintain, test, and reuse than tightly coupled code.
  • The Interface Segregation Principle focuses on reducing potential and actual coupling to unnecessary interfaces; other formulations highlight hiding interfaces you don’t control from application code, reducing coupling to them

Coupling is a subject that we frequently address on our website:

References

Synonyms:
Decoupling
Categories: Field Atlas


« Back to Glossary Index

Share Your Thoughts

This site uses Akismet to reduce spam. Learn how your comment data is processed.