Design Pattern Evangelist Blog

Smart pointers about software design

What Are Cohesion and Coupling?

Cohesion and coupling address when things should and should not be too sticky with one another


Introduction

This blog was originally going to be a continuation of the previous Abstract blog where I planned to follow up with Cohesive Abstractions.

I was close to publishing it, but I realized that I was devoting almost 1,000 words to Cohesion and Coupling, which started to feel like its own separate blog. I’m making a last-minute decision to blog exclusively on Cohesion and Coupling first, and then follow up with Cohesive Abstractions shortly thereafter.

Cohesion and Coupling

Cohesion and Coupling both deal with the nature of how software elements are connected. These aren’t always the easiest concepts to understand, especially when considered together.

The former is about elements that should be connected but often aren’t connected. The latter is about elements that should not be connected but often are connected.

Cohesion

It took me a while before I had a solid understanding of Cohesion.

Elements are cohesive when there’s an intrinsic relationship that connects them. The relationship is often gestalt in that the elements only have meaningful utility when combined as a whole. When an update is needed, most or all elements in the cohesive relationship will require updating.

High Cohesion

Screw and Screwdriver

The intrinsic relationship between screw and screwdriver is an example. Neither is functional without the other. The screwdriver must also match the type of screw. For example, a flathead screwdriver won’t work on a Phillips head screw. If the design changes from a flathead screw to a Phillips head screw, then the screwdriver will need to change from a flathead to a Phillips head likewise.

Nuts and Bolts

Nuts and bolts are cohesive. They only function when screwed together, and when their sizes match.

We want our designs to have high cohesion. That is, software elements with intrinsic relationships should be near one another in the design.

Low Cohesion

Low cohesion occurs when related software elements have been distributed across the design. For example, you’re enhancing an existing behavior, and it requires changes to a dozen files distributed throughout the design. Additionally, the files being updated also contain code that’s not directly related to the behavior that you’re updating.

Low cohesion has at least two concerns:

A common example of low cohesion I’ve seen is state machine behavior (blog TBD) spread across the implementation. This usually happens when a class has a private attribute named something like status or state. State Machine Diagram Its type is often an enum, but I’ve also seen it as a boolean and even a String. Its implied privacy is violated with get and set accessors, which provide direct access to the private attribute.

The Lost Symbol by Dan Brown

Rather than keeping the state machine behavior encapsulated within the class, the class becomes the place where state or status reside. Other classes will access status/state via the get accessor, update status/state with a new value via the set accessor based upon their own business logic code. Not only does this low cohesive design distribute the state machine implementation across far flung regions of the design, it distributes the ability to understand the behavior. It’s like being Robert Langdon in one of Dan Brown’s novels running around desperately looking for clues to see the bigger picture. This makes it more difficult to know what the state machine will do as a whole when the status/state is updated from any place in the implementation.

With cohesive software elements, a change to one element may require a change to the other related cohesive elements, like how a change in screw head causing a change in the screwdriver. Highly cohesive software elements are close to one another, such as in the same package, making it more likely that all required updates will occur consistently.

Interface Cohesion

I consider the methods of an interface cohesive when:

Traditional Create, Read, Update and Delete (CRUD) operations are cohesive in my mind. Remove any of them, and the remaining operations are incomplete. The requirements for one of my features contained Create, Update and Delete requirements for a set of domain elements, but there were no Read requirements. I asked my requirements creator why I was implementing features to manage data when there were no requirements to read the data. Oops.

A cohesive interface tends to follow the Interface Segregation Principle (ISP). An interface that violates ISP contains methods that can be separated into their own cohesive interfaces.

Coupling

Coupling is often uttered in the same breath with cohesion. It took me a while to gain a solid understanding of coupling, especially when used with cohesion. They seemed similar yet different, and I wasn’t sure how to distinguish them.

Coupling is also about connected elements. However, unlike cohesive elements, coupled elements do not have intrinsic relationships.

Reece's Cup

A Reese’s Peanut Butter Cup couples chocolate and peanut butter together. I know for many, a peanut butter cup would be an excellent example of cohesion, but I’m the odd duck who doesn’t like peanut butter cups, even though I like chocolate and peanut butter separately.

We want our designs to have loose coupling. That is, we don’t want unrelated elements to be stuck together too tightly.

A Reese’s Cup would be tightly coupled if it were the only form from which we could get chocolate or peanut butter. Imagine scraping the insides from a Reese’s Cup as the only means to make a peanut butter sandwich.

Tight Coupling

Tight coupling occurs when disparate software concepts are stuck together. For example, the communication framework, business logic and persistence implementations are intertwined in the same method. A change to one of these elements runs a risk of affecting or breaking one of the other non-related coupled elements. A tightly coupled method is often a violation of the Single Responsibility Principle, since there’s more than reason for it to change. Reusing the business logic in another context becomes impossible when it’s tightly coupled with other elements.

Loose Coupling

Many modern software practices, such as Design Patterns, Hexagonal Architecture/Ports & Adapters, and others address tight coupling. They promote loose coupling. The first Design Pattern Principle addresses this, even if it doesn’t mention coupling directly: Program to an interface, not an implementation.

Summary

A good design features high cohesion for those software elements that do change together and loose coupling of software elements that don’t change together.

Without care and consideration, a design can easily degrade into low cohesion and tight coupling, as described above, which are undesirable design traits.

The two pairs of high/loose and low/tight tend to go together. If a design has high cohesion, then it tends to have loose coupling and vice versa. The same applies to the low cohesion/tight coupling pair.

References

Comments

Previous: What Is Abstraction?

Next: DRAFT – Coupling and Cohesion – Take 2

Home: Design Pattern Evangelist Blog