Looks Simple but is not — Two Basic concepts very hard to achieve and maintain

Clemente Morales Fernández
4 min readOct 29, 2020

As developers, we learned the principles of abstraction, encapsulation, polymorphism, and evil inheritance when we met our first Object-oriented programming language.

We were also presented with Coupling and Cohesion as two basic design concepts, “high cohesion and low coupling” as they said. It seemed very simple at the beginning, but looks like is not, we have a lot of projects breaking these basic things. But, why? why are they hard to achieve and then maintain over time? how do we know if our code is cohesive and low coupled?

Let’s explore these two concepts a little more.

Cohesion

This is how a single class is designed with a single, well-focused purpose.

Classes with a well-focused purpose tend to be more reusable than other classes.

Related code should be close to each other to make it highly cohesive.

Sonar has a metric to measure cohesion: LCOM4

LCOM4

Lack of Cohesion in Methods (LCOM4).This metric measures the degree to which methods and fields within a class are related to one another.

LCOM4 = 1: The class is a solid component with all methods and fields related

LCOM4 > 1: The class can be split to …

Coupling

How closely a class or a module supports a single purpose or responsibility. Is the degree of direct knowledge that one element has over another. Low coupling indicates low dependency. Tight coupling means the two classes often change together.

We can analyze coupling using a graph.

Coupling Graph

Method calls and returns form a call graph. The coupling can be analyzed in the code using graphs.

If we create a small diagram with the components/classes/packages and their dependencies, you will see immediately how coupled is your code.

Cohesion and Coupling are also very closely related to other principles

Cohesion and Single responsibility pattern

When a class is highly cohesive it has only one specific job. One Single Responsibility. Cohesive classes are easier to maintain than classes with a lot of responsibilities.

That’s one of the reasons behind the recommendation of avoiding suffixes like manager or util. We don’t know exactly what are the responsibilities of those classes.

How many reasons do you have to go and change a class/module?

DRY and Cohesion

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

Be careful, because this principle not only applies to code repetition but also knowledge, cohesion, the context of the code.

Trying to avoid code repetition for different business domains can make the code coupled with those domains. If we try to reuse the same code for a different domain, is highly possible your code ends up with more than one reason to change.

DRY is about the Cohesive code. If two pieces of code represent the exact same knowledge, they will always change together.

Low Coupling and Principle of Least Knowledge

Also, referred as the Law of Demeter. This principle means to talk only to your immediate friends. Changes in one part of the system should not cascade to other parts.

We should only invoke methods belonging to:

  • The object itself
  • An object passed as a parameter to the method
  • Any Object the method creates or instantiates
  • Any Object-defined as an instance variable in the class

This principle will help us to keep the code less tightly coupled. Our code doesn’t need to know implementation details of the dependencies, we need only limited knowledge.

Be careful, when following this principle because you can start adding more wrappers without noticing, and that means more layers that will impact the runtime performance of your app.

Low coupling using design patterns and interfaces

We can decouple our code by using design patterns and interfaces. We have GOF design patterns, architectural patterns, and design principles. Some of those principles are:

  • Encapsulate the things that change independently and expose them using interfaces.
  • Favor composition over inheritance.
  • Program of interfaces and not implementations. Depend on abstractions, not concrete implementations.
  • SOLID principles.

When a design follows these patterns and principles, new changes will be easier to apply and the cost will be reduced too.

One thing to note here is that even our code will be more flexible, that flexibility will increase the complexity too. So is a trade-off. How much flexibility do we need?

Paradox of complexity

The more flexible the system, the more likely that the system can adapt and evolve as necessary.
Ralph Johnson
“Making everything easy to change makes the entire system very complex”

As flexibility increases, so does the complexity. so the question is how much flexibility do we really need? how much is good enough? Do we really need those abstractions?

Cohesion, Coupling, and Maintenance

How many places did you touch with that last change?

Have you tried moving one of your app features to another module? One of the common problems when trying to move the code to a different module are “utility classes”. Highly coupled code will make the code very hard to maintain.

By aiming for low coupling, you can easily make changes to the internals of classes/modules without worrying about their impact on other parts of the system.

Cohesive classes will make your code easier to change.

Low coupled classes will simplify your testing.

I think cohesion and low coupling is something we can achieve, we just need to spend more time on design, and code reviews, especially when working remotely.

--

--