CMD Guide
HomeOO & Low-Level DesignSOLID Principles

Introduction to the Interface Segregation Principle

The Interface Segregation Principle works by making the type system enforce capability: an interface is a compile-time contract, so the smaller and more focused each interface is, the fewer methods a class is forced to define and the fewer methods a caller is allowed to call — which means a class can only ever promise behaviour it can actually deliver. Robert C. Martin's original phrasing: "Clients should not be forced to depend on methods they do not use." The leverage point is the word forced: in a statically-typed language, implements Printer is a promise to honour every method on Printer, and the compiler will not let you opt out. So a fat interface manufactures lies — methods that exist only to satisfy the compiler and then betray the caller at runtime.

The fat-interface trap, traced

You are asked to model a fleet of office printers. The tempting move is one interface that names every capability any device might have:

public interface Printer {
    void printDocument(String document);
    void scanDocument(String document);
    void faxDocument(String document);
    void stapleDocument(String document);
}

Now a cheap BasicPrinter — which can only print — is still required by the compiler to supply scanDocument, faxDocument, and stapleDocument. It has nothing real to put in them, so it fakes them:

public class BasicPrinter implements Printer {
    @Override public void printDocument(String document) {
        System.out.println("Printing: " + document);
    }
    // The compiler FORCES these three. The hardware has no scanner,
    // no fax line, no stapler, so the only "honest" body is a lie:
    @Override public void scanDocument(String document) {
        throw new UnsupportedOperationException("BasicPrinter cannot scan.");
    }
    @Override public void faxDocument(String document) {
        throw new UnsupportedOperationException("BasicPrinter cannot fax.");
    }
    @Override public void stapleDocument(String document) {
        throw new UnsupportedOperationException("BasicPrinter cannot staple.");
    }
}

Why the naive version is wrong

The type says BasicPrinter is a Printer and Printer says it can scan — so the type is asserting something false. The failure is not the exception itself; it is that the false promise is invisible until runtime. Trace what happens to an office-automation routine that accepts the wide type:

StepCodeWhat the compiler seesWhat actually happens at runtime
1Printer p = new BasicPrinter();OK — BasicPrinter is a Printerfine
2p.printDocument("q3-report.pdf");OKprints — "Printing: q3-report.pdf"
3p.scanDocument("contract.pdf");OKscan is on Printerthrows UnsupportedOperationException
4batch job aborts mid-runtwo prior pages already printed; no rollback

At step 3 the compiler is useless — it green-lights a call that cannot succeed, because the contract BasicPrinter signed claims scanning is available. The whole point of static typing (catch the error before it ships) has been thrown away. ISP fixes this upstream: if scan is not on the type you hold, line 3 will not compile, and the bug can never reach production.

diagram
diagram

The mechanism in one move

You split the fat Printer into role interfaces, one per capability, and each device implements only the roles its hardware actually supports:

public interface Printable   { void printDocument(String d); }
public interface Scannable   { void scanDocument(String d); }
public interface Faxable     { void faxDocument(String d); }
public interface Stapleable  { void stapleDocument(String d); }

public class BasicPrinter implements Printable {
    @Override public void printDocument(String d) {
        System.out.println("Printing: " + d);
    }
} // no fake methods, no exceptions, nothing to lie about

public class OfficeAllInOne implements Printable, Scannable, Faxable, Stapleable {
    @Override public void printDocument(String d) { /* ... */ }
    @Override public void scanDocument(String d)  { /* ... */ }
    @Override public void faxDocument(String d)   { /* ... */ }
    @Override public void stapleDocument(String d){ /* ... */ }
}

A function that only needs to scan now declares void archive(Scannable s) — and you literally cannot hand it a BasicPrinter. The error that used to surface as a 2 a.m. pager alert is now a red squiggle in the editor. (The full refactor — including how callers compose multiple roles — is the next lesson; here the goal is to see precisely why the move is forced.)

Pitfalls

When to apply ISP — and when not to

Reach for it when you see the concrete smells: a method body that is throw new UnsupportedOperationException or an empty stub; an implementer that uses far fewer methods than its interface declares; two unrelated callers that each touch a disjoint slice of one big interface; or a change to method X forcing recompilation/retesting of classes that never call X (the fat interface couples them). Any one of these means a client depends on methods it does not use.

The trade-off is interface count and indirection. Segregating turns one named type into four; a fully-featured device now reads implements Printable, Scannable, Faxable, Stapleable, and a caller needing two capabilities must accept a composed type (<T extends Printable & Scannable> in Java) or take two parameters. You buy compile-time honesty and decoupling; you pay in more declarations to navigate.

Versus the named alternatives

The senior call, in one line: choose ISP when implementers legitimately differ in which methods they can honour and you want the compiler to enforce it; prefer one fat interface when the capability set is genuinely uniform across all implementers and extra types would only add noise.

Takeaways


Sources: Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices (the origin of ISP and the Xerox print-system case that inspired it) and Clean Architecture (2017); the Java Language Specification on interface implementation and intersection types (&); and the standard library's java.util.List / UnsupportedOperationException contract as the canonical fat-interface counter-example. Re-authored and deepened for this guide — the original lesson showed the bloated-Printer smell correctly but stopped at the problem; this version adds the type-system mechanism, a step-by-step runtime-failure trace, an interface-decomposition diagram, pitfalls, and the selection trade-offs against fat interfaces, base classes, and runtime capability checks.

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

Stuck on Introduction to the Interface Segregation Principle? 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 **Introduction to the Interface Segregation Principle** (OO & Low-Level Design) and want to truly understand it. Explain Introduction to the Interface Segregation Principle 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 **Introduction to the Interface Segregation Principle** 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 **Introduction to the Interface Segregation Principle** 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 **Introduction to the Interface Segregation Principle** 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