CMD Guide
HomeOO & Low-Level DesignSOLID Principles

Coupling and its Relation to the Single Responsibility Principle SRP

Coupling is the strength of the assumptions one module makes about another: every name a class references at compile time — a type, a method signature, a field, a constructor — is a wire soldered between them, and the more wires (and the more concrete the things on the other end), the more a change on one side forces a recompile, retest, or rewrite on the other. Reducing coupling is mechanically about cutting wires or replacing concrete wires with abstract ones so a change stays local.

This page fixes a common sloppiness: people call "injecting a DatabaseManager into Student" low coupling. It is better than hard-wiring the DB inside Student, but Student still imports and names the concrete DatabaseManager — that is only weak decoupling. The universal-remote analogy everyone draws actually requires an interface (the Dependency Inversion move), which the naive version never introduces.

Three levels of coupling, made precise

The word "coupling" hides at least three distinct degrees. Naming them stops the overstatement:

LevelWhat Student depends onSwap the DB without editing Student?Test Student without a real DB?
L0 — Internal
logic baked into Student.save()
JDBC, SQL strings, connection — all inside StudentNo — edit StudentNo
L1 — Concrete injected
constructor takes DatabaseManager
the concrete class DatabaseManager (compile-time import)No — Student still names the concrete typeOnly if you can construct a real DatabaseManager
L2 — Abstraction injected
constructor takes StudentRepository interface
an interface; the concrete impl is chosen elsewhereYes — pass any implementationYes — pass a fake/mock

The original lesson moved L0 → L1 and called it the win. The real win — the one the universal remote depicts — is L1 → L2.

diagram
diagram

Why the naive version is wrong

Constructor injection of a concrete type is genuinely useful — it makes the dependency explicit and lets you reuse one DatabaseManager instance. But it does not deliver what the analogy promised. Concretely:

The correct fix introduces an interface and inverts the arrow:

java
// The abstraction Student will depend on (the "IR protocol").
public interface StudentRepository {
    void save(String studentId);
}

// One concrete implementation, chosen at the composition root, NOT by Student.
public class JdbcStudentRepository implements StudentRepository {
    @Override
    public void save(String studentId) {
        System.out.println("Connecting to database...");
        System.out.println("Saving student with ID: " + studentId);
        // real JDBC here
    }
}

// Student now names ONLY the interface — no import of any concrete DB class.
public class Student {
    private String studentId;
    private final StudentRepository repo;

    public Student(StudentRepository repo) {   // depends on abstraction
        this.repo = repo;
    }

    public String getStudentId() { return studentId; }
    public void setStudentId(String studentId) { this.studentId = studentId; }

    public void save() {
        repo.save(studentId);                  // delegates through the interface
    }
}

Now the only thing knitting Student to a database is a single method signature, save(String). Swapping Postgres for an in-memory store, or for a test fake, never touches Student.

Worked trace: a schema change ripples (or doesn't)

Suppose product asks: "store students in Postgres instead of the old MySQL helper, and add a tenantId column." Trace which files each design forces you to edit and recompile.

Step / changeL0 (baked in)L1 (concrete injected)L2 (interface)
Rewrite the SQL / driveredit Student.save()edit DatabaseManageredit JdbcStudentRepository
Does Student recompile?Yes — it holds the SQLYes — it imports the concrete class*No — interface unchanged
Add a 2nd backend (e.g. cache)fork Student logicchange Student's field typeadd a new impl class only
Unit-test save() with no DBimpossibleneed real DatabaseManagernew Student(fakeRepo)
Files touched for the whole change1 (but high-risk)22 — and Student is not one of them

*Even if the method signature is unchanged, Student is in the recompile set because the type it names was edited; in L2 the type it names (the interface) did not change, so it stays untouched and its tests stay green.

Coupling and SRP: two knobs, one habit

SRP says a class should have one reason to change. The mechanism linking it to coupling: each external concretion a class names is an extra reason to change. The L0 Student changes when student rules change and when DB rules change — two reasons, a direct SRP violation, caused by coupling DB code into it. Pulling DB logic out (any of L1/L2) restores one reason for Student. But only L2 also gives low coupling, because only L2 removes the concrete name. So: SRP is about how many reasons to change; coupling is about how far each change propagates. Splitting responsibilities fixes the first; depending on abstractions fixes the second. They reinforce each other but are not the same fix.

A second, larger instance makes the count concrete. A ReportGenerator that imports a PDF library, an email client, a DB handle, and the tax-rules module has four foreign concretions wired in — and at least three actors who can force an edit: a tax-law change, an email-template change, and a storage change all land in the same file. Counting the actors who can make you edit a class is the quickest SRP test in review: more than one and the class is a split candidate (here, into TaxCalculator, ReportRenderer, and Notifier, wired together by an application service). Notice the two knobs move together: each responsibility you extract also removes a foreign import, so the split lowers coupling at the same time.

Pitfalls

When to introduce the interface — and when not to

This is a decision, not a default. The signals that point toward L2 (interface + injection):

Trade-offs vs. the alternatives:

One-liner: choose the interface when you must substitute or test the dependency; prefer plain concrete injection when the collaborator is stable and singular; keep it inline only for trivial, I/O-free code.

Scenario: a 30-line CLI that prints one student to stdout — L0 or L1 is fine; an interface here is overengineering. The same Student in a service with a 200-test suite that must run without a database — L2 is mandatory, because the test seam alone pays for the extra type.

Takeaways


Sources: Robert C. Martin, Clean Architecture (2017) and Agile Software Development: Principles, Patterns, and Practices (SRP & DIP); Stevens, Myers & Constantine, "Structured Design" (IBM Systems Journal, 1974) for the original afferent/efferent coupling taxonomy; Martin Fowler, "Inversion of Control Containers and the Dependency Injection pattern" (2004) on composition roots; the Java code was compiled and traced against the worked example. Re-authored and deepened for this guide — the prior version mislabeled concrete constructor injection as "low coupling" and never introduced the interface its own analogy demanded; the decorative image was dropped for a mechanism diagram.

🤖 Don't fully get this? Learn it with Claude

Stuck on Coupling and its Relation to the Single Responsibility Principle SRP? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Coupling and its Relation to the Single Responsibility Principle SRP** (OO & Low-Level Design) and want to truly understand it. Explain Coupling and its Relation to the Single Responsibility Principle SRP from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Coupling and its Relation to the Single Responsibility Principle SRP** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Coupling and its Relation to the Single Responsibility Principle SRP** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Coupling and its Relation to the Single Responsibility Principle SRP** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes