On this page

Interface Segregation Principle (ISP)

The Interface Segregation Principle (ISP) is the fourth of the five SOLID principles. It says that interfaces should be small and focused, so a class is never forced to implement methods it does not need.

Definition

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

If an interface is big, every class that implements it must write all its methods, even the ones that make no sense for it. The fix is to split the big interface into smaller ones, each for one role.

Analogy: think of a restaurant menu. A fat interface is a single giant menu handed to everyone, including a customer who wants only a drink. A better design is separate menus: food, drinks, and desserts. Each person gets only the menu they need.

Why It Matters

A fat interface causes these problems:

  • Empty or fake methods. Classes write throw new UnsupportedOperationException() just to compile.
  • Unneeded changes. If a method you never use changes, your class must still be recompiled and re-tested.
  • Hard to test and mock. You must mock a dozen methods to test one.
  • LSP violations. Fake implementations break the promise of the interface.

A Violation

public interface Worker {
void work();
void eat();
void sleep();
}
public class HumanWorker implements Worker {
public void work() { System.out.println("Working"); }
public void eat() { System.out.println("Eating"); }
public void sleep() { System.out.println("Sleeping"); }
}
public class RobotWorker implements Worker {
public void work() { System.out.println("Working"); }
public void eat() {
throw new UnsupportedOperationException("Robots do not eat");
}
public void sleep() {
throw new UnsupportedOperationException("Robots do not sleep");
}
}

RobotWorker is forced to implement eat() and sleep(), which make no sense for it. Any code that calls eat() on a Worker can crash.

The Fix

Split the interface into small role-based interfaces. A class implements only the roles it really has.

public interface Workable {
void work();
}
public interface Eatable {
void eat();
}
public interface Sleepable {
void sleep();
}
public class HumanWorker implements Workable, Eatable, Sleepable {
public void work() { System.out.println("Working"); }
public void eat() { System.out.println("Eating"); }
public void sleep() { System.out.println("Sleeping"); }
}
public class RobotWorker implements Workable {
public void work() { System.out.println("Working"); }
}

Now RobotWorker has no empty methods. Code that needs only work depends on Workable, so it works with both humans and robots.

A Realistic Example: Repository

Fat interfaces are common in backend code. Here is a single interface that every data class must implement:

// Violation: a read-only report class must also implement the write methods
public interface Repository<T> {
T findById(long id);
List<T> findAll();
void save(T item);
void update(T item);
void delete(long id);
}
// Better: split by what clients actually need
public interface ReadRepository<T> {
T findById(long id);
List<T> findAll();
}
public interface WriteRepository<T> {
void save(T item);
void update(T item);
void delete(long id);
}
// A reporting service depends only on reading
public class ReportService {
private final ReadRepository<Order> orders;
public ReportService(ReadRepository<Order> orders) {
this.orders = orders;
}
}

ReportService can only read. It cannot delete data by mistake, and tests need to fake only two methods.

The Idea Behind It

ISP is about the client, not the implementer. Ask: what does the code that uses this interface actually need? Shape the interface around the client's needs, not around everything the implementation can do.

Spotting a Violation

  • Implementations that throw UnsupportedOperationException or have empty method bodies.
  • An interface with many unrelated methods, often named Manager, Service, or Handler.
  • A class that implements an interface but uses only two of its ten methods.
  • A tiny change to the interface forces many classes to change, even classes that never call that method.
  • Tests need large mocks just to check one method.

Common Mistakes

1. Going too far. One-method interfaces for every single method create a pile of tiny interfaces that are hard to navigate. Group methods that are always used together. Split by role, not by method.

2. Splitting by the implementer, not the client. Look at who uses the interface, and let their needs decide the split.

3. Hiding the problem with default methods. A Java default method that throws or does nothing still forces a bad method onto every class. It is a workaround, not a fix.

ISP and Other Principles

PrincipleLink to ISP
Single ResponsibilitySRP is about classes, ISP is about interfaces. Both say "keep it focused"
Liskov SubstitutionFat interfaces push classes to fake methods, which breaks LSP
Dependency InversionHigh-level code depends on small abstractions, which ISP helps create

Summary

  • ISP: no class should be forced to depend on methods it does not use.
  • Prefer many small, role-based interfaces over one big one.
  • Shape each interface around what the client needs.
  • Warning signs: empty methods, UnsupportedOperationException, and huge interfaces with unrelated methods.
  • Do not overdo it: group methods that are always used together.
  • It also makes code easier to test and safer to change.

Interview Answer: What Is the Interface Segregation Principle?

The Interface Segregation Principle says that no class should be forced to implement methods it does not use. So we prefer several small, focused interfaces over one large interface.

For example, a Worker interface with work(), eat(), and sleep() forces a RobotWorker to implement eat() and sleep(), usually by throwing an exception. I would split it into Workable, Eatable, and Sleepable, so a robot implements only Workable, and a human implements all three.

I design interfaces around what the client needs. For example, a reporting service should depend on a read-only repository, not on one that can also delete. This keeps code easier to test and safer to change. I avoid going too far, though: I group methods that always belong together, so I do not end up with dozens of one-method interfaces.

Next TopicDependency Inversion Principle (DIP)