Home/Learn/Low Level Design/Cohesion & Coupling

Cohesion & Coupling

Intermediate
OOP & UML Fundamentals

Understand why high cohesion and low coupling are the bedrock of maintainable OOP design, with Java examples showing each extreme.

Overview

Cohesion measures how strongly the responsibilities within a module are related to each other. Coupling measures how much one module depends on another. The engineering goal is always: high cohesion (each class does one focused thing) and low coupling (changes in one class do not cascade through the system). Cohesion types range from Functional (best) down to Coincidental (worst). Coupling types range from Content (worst, directly modifying another class's fields) up to Message (best, only interface calls). SOLID principles, design patterns, and dependency injection all exist to enforce high cohesion and low coupling at scale.

Requirements Analysis

Cohesion spectrum (best to worst): Functional → Sequential → Communicational → Procedural → Temporal → Logical → Coincidental. Coupling spectrum (best to worst): Message → Data → Stamp → Control → Common → Content. Goal: Functional cohesion + Message/Data coupling for every class in the system.

Requirements
// High cohesion: class has ONE well-defined purpose
// Low coupling: class depends on abstractions, not concrete details
// Violating either leads to fragile, untestable, hard-to-change code

Core Classes & Relationships

Bad example: UserService that handles authentication, email, PDF generation, and DB access in one class (Coincidental cohesion + Content coupling). Good example: separate AuthService, EmailService, UserRepository each with Functional cohesion, communicating through interfaces (Message coupling). OrderProcessor tightly coupled to concrete MySqlOrderDao vs loosely coupled through OrderRepository interface.

Java — enums & interfaces
// ❌ Coincidental Cohesion — unrelated responsibilities crammed into one class
public class GodService {
    public void authenticateUser(String email, String pwd) { /* auth logic */ }
    public void sendEmail(String to, String subject)       { /* email logic */ }
    public byte[] generatePdf(Order order)                 { /* PDF logic */ }
    public void saveOrder(Order order)                     { /* DB logic */ }
    public void calculateTax(double amount)                { /* tax logic */ }
    // Changing email template forces recompile of the entire class
    // Changing DB schema forces retesting of all unrelated methods
}

// ❌ Content Coupling (worst) — directly accessing another class's internals
public class OrderProcessor {
    public void process(Order order) {
        // Directly reading private field — breaks encapsulation completely
        if (order.status == "PENDING") {   // accessing package-private field!
            order.status = "PROCESSING";   // mutating state directly
        }
    }
}

// ❌ Tightly coupled to concrete class (Common coupling via shared mutable state)
public class ReportService {
    private final MySqlOrderDao dao = new MySqlOrderDao(); // hard dependency on impl
    // Cannot test without real MySQL; cannot swap to PostgreSQL without editing class
}

Java Implementation

Refactored: each class has Functional cohesion (one reason to change). Message coupling via interfaces: OrderProcessor depends on OrderRepository interface, not MySqlOrderDao. UserRegistrationService orchestrates via constructor-injected interfaces. Changes to email template, DB implementation, or tax rules are isolated to their respective classes.

Java — core classes
// ✅ Functional Cohesion — each class has exactly one responsibility

// Authentication only
public class AuthService {
    private final PasswordEncoder encoder;
    private final UserRepository users;
    public AuthService(PasswordEncoder encoder, UserRepository users) {
        this.encoder = encoder; this.users = users;
    }
    public boolean authenticate(String email, String rawPassword) {
        return users.findByEmail(email)
            .map(u -> encoder.matches(rawPassword, u.getPasswordHash()))
            .orElse(false);
    }
}

// Email only
public class EmailService {
    public void sendWelcomeEmail(String to) {
        System.out.println("Sending welcome email to: " + to);
    }
}

// ✅ Message Coupling (best) — depends only on interface, communicates via method calls
public interface OrderRepository {
    void save(Order order);
    Optional<Order> findById(String id);
}

public class OrderProcessor {
    private final OrderRepository repository; // interface — not MySqlOrderDao

    public OrderProcessor(OrderRepository repository) {
        this.repository = repository; // injected — decoupled
    }

    public void process(Order order) {
        order.markProcessing();          // method call — no direct field access
        repository.save(order);          // message coupling — interface only
    }
}

// ✅ Low coupling via DI — orchestrator composes everything
public class UserRegistrationService {
    private final UserRepository userRepo;
    private final EmailService emailService;
    private final AuthService authService;

    public UserRegistrationService(UserRepository ur, EmailService es, AuthService as) {
        this.userRepo = ur; this.emailService = es; this.authService = as;
    }

    public User register(String email, String password) {
        User user = userRepo.save(User.of(email, password));
        emailService.sendWelcomeEmail(email);
        return user;
        // Each dependency can be replaced independently — zero cascade on change
    }
}

// ✅ Measuring cohesion in practice:
// "Can I describe this class's responsibility in one sentence without 'and' or 'or'?"
// AuthService: "Verifies user credentials" → YES — functionally cohesive
// GodService: "Handles auth and email and PDF and DB" → NO — coincidentally cohesive

Key Points to Remember

  • 1Functional cohesion: every method and field in the class exists to serve one single purpose — the gold standard.
  • 2Content coupling (worst): directly reading or writing another class's private fields — always a design smell.
  • 3Message coupling (best): a class only calls public methods on interfaces it depends on — no knowledge of internals.
  • 4The one-sentence rule: if you need "and" or "or" to describe a class, it has low cohesion and should be split.

Interview Questions

Sign in to ask Aria
1

What is the difference between cohesion and coupling? Why do we want high cohesion and low coupling?

EasyAmazon
2

Give an example of Content coupling and explain how to refactor it to Message coupling.

MediumGoogle
3

How do SOLID principles enforce high cohesion and low coupling?

MediumMicrosoft

Ask Aria about Cohesion & Coupling

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…