cohesion-y-acoplamiento

What Are Cohesion and Coupling Metrics Between Classes

  • 4 min

Cohesion and coupling are two ways of measuring how well-organized a software design is.

When we start programming, our main concern is simply “making it work”. Over time, we realize that writing code that works is only half the battle. The other half is making code that is maintainable, scalable, and robust.

These two terms often go hand in hand and are repeated like a mantra in software design: high cohesion and low coupling.

These concepts are not exclusive to Object-Oriented Programming, but it is in OOP where they become critically relevant for designing efficient classes and systems.

What do they really mean? Why are they so important? And, above all, how do we apply them in our daily work? Let’s look at it in depth 👇.

What is Cohesion?

Cohesion is the measure of how related and focused the responsibilities of a module, class, or function are.

In simple terms: How much sense does it make for these things to be together?

  • A class with High Cohesion is one that is dedicated to a single task or a set of closely related tasks.
  • Conversely, a class with Low Cohesion (or “weak cohesion”) tries to do too many disparate things, becoming what we call a “God Object”.

Suppose we have a class OrderManager. If this class:

  1. Validates the order data.
  2. Connects to the database to save the order.
  3. Generates a PDF with the invoice.
  4. Sends an email to the customer.

We have a low cohesion problem. This class does everything. If the way emails are sent changes, we have to modify the order class. If the database changes, we modify the order class.

To achieve high cohesion, we should separate these responsibilities. OrderManager should only orchestrate the process, delegating to:

  • OrderValidator (Business logic)
  • OrderRepository (Persistence)
  • InvoiceGenerator (Documents)
  • NotificationService (Emails)

What is Coupling?

Coupling is the measure of how dependent modules are on each other. It is the degree to which a change in one class forces changes in another.

Although there are many levels, we can simplify it into:

  1. Tight Coupling: Occurs when a class depends on the concrete details of another. For example, directly instantiating a class with new inside another.
  2. Loose Coupling: Occurs when a class depends on abstractions (interfaces or abstract classes) rather than concrete implementations.

The goal: High cohesion and low coupling

The goal of a good design is to maximize cohesion and minimize coupling.

MetricGoalWhy?
CohesionHighMakes it easier to understand what the class does. Reduces complexity.
CouplingLowMakes changes easier. If I modify A, I don’t break B. Facilitates testing.

Let’s look at a practical code example to illustrate the difference.

Here we have a class with low cohesion (does many things) and high coupling (depends on the console and a specific way of saving).

// Bad design: Low cohesion and High coupling
public class OrderProcessor
{
    public void Process(Order order)
    {
        // 1. Validation logic mixed in
        if (order.Amount <= 0)
        {
            Console.WriteLine("Error: Invalid amount"); // Coupled to console
            return;
        }

        // 2. Database logic mixed in (Hardcoded)
        // Suppose direct SQL code here...
        Console.WriteLine("Saving to SQL Server...");

        // 3. Notification logic mixed in
        Console.WriteLine("Sending email to user...");
    }
}
Copied!

If we want to change the database to a text file, or send an SMS instead of an email, we have to modify and recompile this class, risking breaking the validation.

We’ll apply high cohesion by separating responsibilities and low coupling by using interfaces.

// Define contracts (Interfaces) to lower coupling
public interface IOrderRepository { void Save(Order o); }
public interface INotifier { void Send(string message); }

// Coordinator class with High Cohesion
public class OrderProcessor
{
    private IOrderRepository _repo;
    private INotifier _notifier;

    // Dependency Injection (Low Coupling)
    // We don't do 'new', it's passed to us.
    public OrderProcessor(IOrderRepository repo, INotifier notif)
    {
        _repo = repo;
        _notifier = notif;
    }

    public void Process(Order order)
    {
        if (order.Amount <= 0)
            throw new Exception("Invalid order"); // Basic validation

        _repo.Save(order);       // Delegate persistence
        _notifier.Send("Ok");   // Delegate notification
    }
}
Copied!
  • Now OrderProcessor has high cohesion: its only responsibility is to coordinate the process.
  • And it has low coupling: it doesn’t know if we save to SQL or a file, nor if we notify by email or WhatsApp. It only knows that there is something that fulfills the interface.