Skip to content

Interface Segregation Principle (ISP)

No caller should be forced to depend on methods it never uses. Split fat interfaces by the role each caller plays.

Builds on Interfaces

Stage 2core2 min read

The Machine interface describes an all-in-one office machine:

  • print(Document doc)
  • scan(Document doc)
  • copy(Document doc)

The interface is easy to read, but it makes one big promise: every Machine can print, scan and copy.

Problem

SimplePrinter can only print. But it implements Machine, so Java forces it to write scan() and copy() too. Both methods just throw UnsupportedOperationException, while the interface tells callers they work. Its print() is empty, and copy() has a copy and paste message that says "Scan not supported".

Step by step, the failure looks like this:

  1. A caller receives a variable typed as Machine.
  2. The type has scan(), so the call compiles.
  3. The object behind it is a SimplePrinter.
  4. The program crashes at runtime, because a SimplePrinter can never scan.

If the type had honestly described the smaller ability, the compiler would have caught this mistake before the program ran.

Why It Violates ISP

No client should be forced to depend on methods it does not use.

Here, client means caller, the code that uses the interface. A caller that only prints should depend on a type that only offers printing. The big Machine interface also hurts implementers, because SimplePrinter must fake operations it cannot do.

This design causes several practical problems:

  • unsupported operations fail late, at runtime;
  • a caller can ask a basic printer to scan by accident;
  • tests and fakes must handle methods they do not need;
  • changing scan behavior affects every class that uses Machine;
  • throwing methods also break LSP (the Liskov Substitution Principle: a subtype must work wherever its parent type is expected).

A better split uses Printer, Scanner, and Copier. A simple printer implements only Printer. An all-in-one device can implement all three.

Method count alone is not the test. If every caller and implementation really uses all three operations, Machine may be fine. The split helps here because the capabilities and callers differ.

See GoodCode for the focused interfaces and ISP-Notes.md for the full guide.

In this topic

Before code4 files

BadCode/Document.java4 linesGitHub
package SOLID.ISP.BadCode;public class Document {}
BadCode/Machine.java8 linesGitHub
package SOLID.ISP.BadCode;public interface Machine {    void print(Document doc);    void scan(Document doc);    void copy(Document doc);}
BadCode/MultiPurposeMachine.java21 linesGitHub
package SOLID.ISP.BadCode;// Here the Multipurpose machine uses all the machine interfacepublic class MultiPurposeMachine implements Machine {    @Override    public void print(Document doc) {        System.out.println("Printing document...");    }    @Override    public void scan(Document doc) {        System.out.println("Scan document...");    }    @Override    public void copy(Document doc) {        System.out.println("Copy document...");    }}
BadCode/SimplePrinter.java24 linesGitHub
package SOLID.ISP.BadCode;/** * This SimplerPrinter just want to simple work which is printing * but we are forced to implement the method which wont be available in the * SimplePrinter * */public class SimplePrinter implements Machine {    @Override    public void print(Document doc) {}    @Override    public void scan(Document doc) {        throw new UnsupportedOperationException("Scan not supported");    }    @Override    public void copy(Document doc) {        throw new UnsupportedOperationException("Scan not supported");    }}

Done with this topic?

How well do you know it?

PreviousLSP - The Liskov Substitution Principle
Practise 6 questions