# Interface Segregation Principle (ISP) The **Interface Segregation Principle (ISP)** is the "I" in [[SOLID Principles]]. It states that **no client should be forced to depend on methods it does not use** — large, general-purpose interfaces should be split into smaller, role-specific ones so that consumers only see (and depend on) the operations they actually need. Coined by [[Robert C. Martin]] in the late 1990s during work at Xerox, where a single fat `Job` class had grown to serve every operation any subsystem might need (printing, stapling, copying, faxing). Each subsystem had to recompile whenever any unrelated method changed, even though most subsystems used only a handful of methods. ISP is the antidote: split the surface so each consumer depends on a narrow contract. ## Why it matters - **Compile-time decoupling**: clients only recompile when *their* contract changes. - **Test simplicity**: stubbing or mocking a 3-method interface is trivial; mocking a 30-method "swiss army" interface is painful and brittle. - **Forces honest design**: the act of segregating reveals which roles actually exist in the system. Often a fat interface was fat because two unrelated *actors* were sharing it — same diagnostic as a [[Single Responsibility Principle (SRP)]] violation, viewed from the contract side. - **Reduces accidental coupling**: a client that depends on a narrow contract can be reused with any implementation of *that* contract, not just the original fat class. ## "Fat" interfaces — the smell You're looking at an ISP violation when: - A class implements an interface but throws `UnsupportedOperationException` (or returns null/false) for some methods because they don't apply. - Different consumers of the same interface use disjoint subsets of its methods. - Adding a method to the interface forces edits in classes that have nothing to do with the new behavior. - Mocks for tests routinely stub out 80% of an interface's methods because the test only cares about 2 of them. - The interface name is a noun cluster (`UserAccountServiceManager`) instead of a role (`PasswordResetter`). ## Refactoring: role interfaces The classic refactoring is to split a fat interface into multiple **role interfaces**, each named after the *role* it captures: ``` // Before interface MultiFunctionDevice { print(doc) scan(doc) fax(doc) staple(doc) copy(doc) } // After interface Printer { print(doc) } interface Scanner { scan(doc) } interface Fax { fax(doc) } interface Stapler { staple(doc) } interface Copier { copy(doc) } ``` A real device class can implement all five if it does all five. A simple receipt printer implements only `Printer`. Code that only prints depends on `Printer` — not on a contract that knows about faxing. Many languages (Go's structural typing, TypeScript's structural types, Rust traits) make role interfaces extremely cheap. In nominal-type languages (Java, C#) the cost is a few extra interface declarations. ## ISP in dynamically typed languages In Python, Ruby, JavaScript, and similar languages, there are no compile-time interface declarations. ISP still applies, just shifted in time: - The role of "interface" is played by the *set of methods a function actually calls* on its argument. - A function that calls `obj.print()` requires only a printable; one that calls `obj.print(); obj.staple()` requires both. The contract is implicit. - Type stubs / `Protocol` (PEP 544) / TypeScript interfaces let you make these implicit role contracts explicit. ISP discipline in dynamic languages is mostly: don't pass huge objects when callees need narrow capabilities — pass the narrow capability directly. This nudges toward function composition and small data shapes. ## ISP vs SRP These two are often confused: | | [[Single Responsibility Principle (SRP)]] | Interface Segregation Principle (ISP) | |---|---|---| | **Subject** | Modules / classes | Interfaces / contracts | | **Diagnostic** | This class serves multiple actors | This interface forces clients to depend on methods they don't use | | **Direction** | Producer-side: who maintains this? | Consumer-side: who uses this? | A class can satisfy SRP (one cohesive responsibility) yet violate ISP (its public surface still bundles methods used by different consumers). The two principles attack the same problem — coupling — from different sides. ## ISP and the Dependency Inversion Principle The right level of segregation is dictated by what consumers actually need. This dovetails with [[Dependency Inversion Principle (DIP)]]: the abstraction is owned by the consumer; if you find yourself segregating an interface based on what an existing implementation happens to provide, you've inverted the inversion. Define interfaces from the *client's* perspective, not the supplier's. ## When NOT to segregate - The interface is genuinely cohesive — every method serves the same role and is used together (e.g., `Iterator { hasNext(); next() }` — splitting it would be silly). - Only one consumer exists and probably always will. Premature segregation is just another premature abstraction. - The interface is a third-party / framework contract you don't own. ISP earns its place when fat interfaces are causing real coupling pain. Don't segregate for its own sake. ## References - Robert C. Martin — *The Interface Segregation Principle* (1996, C++ Report) - Robert C. Martin — *Agile Software Development: Principles, Patterns, and Practices* (2002), Chapter 12 - Martin Fowler — *Role Interface* — https://martinfowler.com/bliki/RoleInterface.html ## Related - [[SOLID Principles]] - [[Single Responsibility Principle (SRP)]] - [[Open Closed Principle (OCP)]] - [[Liskov Substitution Principle (LSP)]] - [[Dependency Inversion Principle (DIP)]] - [[Loose Coupling]] - [[High Cohesion]] - [[Composition over Inheritance]] - [[Robert C. Martin]]