Interface Segregation Principle

Many Small Interfaces Instead of One Large Interface

The Interface Segregation Principle (ISP) states that no class should be forced to implement methods that it does not use.

“Clients should not be forced to depend upon interfaces that they do not use.” — Robert C. Martin

Simply put:

Many small, specialized interfaces are better than one large, general-purpose interface.

If a class implementing an interface is forced to define methods that make no sense for it, that is a sign that the interface is too broad and violates ISP.

Real-Life Example: How to Understand It?

Imagine you are a new employee signing an employment contract.

Instead of receiving a standard software developer contract, HR hands you an agreement that requires you to:

  • write code,
  • clean the office,
  • conduct workplace safety training,
  • work at the reception desk,
  • repair the photocopier.

Of course, you could sign it, but what about the responsibilities that have nothing to do with your role?

What would you do with the clause about repairing photocopiers if you’ve never even touched one?

You would somehow need to handle it, most likely by leaving it blank or writing:

“Not applicable.”

And that is exactly where the problem begins.

The contract says you are responsible for something even though you do not actually do it.

Programming works the same way.

When a class implements an interface, it commits to providing implementations for all of its methods. If the interface is too broad, the class must somehow deal with methods that do not apply to it—typically by throwing an exception or leaving the method body empty.

That is an ISP violation.

Instead of one “contract for everything,” it is much better to have several specialized contracts:

  • a contract for software developers (coding, code reviews),
  • a contract for cleaning staff (cleaning and maintenance),
  • a contract for receptionists (guest services and phone support).

Everyone signs only the contract that matches their actual responsibilities.

No sections are empty.

No obligations are fake.

A Thought Exercise

Let’s imagine a human resources system.

What happens if we create one generic Employee interface containing methods for every possible role?

Let’s look at the code.

Example — ISP Violation

Suppose we are building a factory workforce management system.

The factory has three types of workers:

  • a production worker who operates machinery,
  • a manager who attends meetings and manages teams,
  • an industrial robot that performs manufacturing tasks but neither eats nor takes breaks.

In the first approach, we create a single interface for everyone:

// One “big contract” for every worker in the factory
interface Employee {
 
    // Every worker can work
    void work();
 
    // Every worker takes a lunch break
    void takeLunchBreak();
 
    // Every worker attends board meetings
    void attendBoardMeeting();
}

Now let’s implement various worker types.

Production Worker

class ProductionWorker implements Employee {

 

    @Override
    public void work() {
        System.out.println(“I operate a machine on the factory floor.”);
    }

 

    @Override
    public void takeLunchBreak() {
        System.out.println(“I go to the cafeteria for lunch.”);
    }

 

    @Override
    public void attendBoardMeeting() {
        // Problem: production workers do not attend board meetings!
        throw new UnsupportedOperationException(
            “Production workers do not attend board meetings!”
        );
    }
}


Industrial Robot
class Robot implements Employee {
 
    @Override
    public void work() {
        System.out.println(“I perform tasks on the assembly line.”);
    }
 
    @Override
    public void takeLunchBreak() {
        // Problem: robots do not eat!
        throw new UnsupportedOperationException(
            “Robots do not need lunch breaks!”
        );
    }
 
    @Override
    public void attendBoardMeeting() {
        // Problem: robots do not attend meetings!
        throw new UnsupportedOperationException(
            “Robots do not attend meetings!”
        );
    }
}

Manager

class Manager implements Employee {

 

    @Override
    public void work() {
        System.out.println(
            “I review reports and manage the team.”
        );
    }

 

    @Override
    public void takeLunchBreak() {
        System.out.println(
            “I have lunch with a client.”
        );
    }

 

    @Override
    public void attendBoardMeeting() {
        System.out.println(
            “I lead the quarterly board meeting.”
        );
    }
}
What’s Wrong with This Design?

Classes Throw Exceptions for Unsupported Methods

The Robot class implements takeLunchBreak(), but the only sensible thing it can do is throw an exception.

Anyone calling this method gets a runtime error instead of a compile-time indication that the operation is invalid.

The Interface Lies

When somebody sees:

Robot implements Employee

they naturally assume that a robot can do everything an Employee can do, including taking lunch breaks.

That assumption is false.

Changes Affect Everyone

Suppose we add:

void signUnionAgreement();

to the Employee interface.

Now every implementing class—including Robot—must provide an implementation, even though robots will never sign any agreement.

Example — ISP-Compliant Design

Let’s improve the design.

Instead of one large contract, we create several smaller interfaces, each describing a single capability.

Work Capability
interface Workable {
    void work();
}
Eating Capability
interface Eatable {
    void takeLunchBreak();
}
Management Capability
interface Manageable {
    void attendBoardMeeting();
}

Now each class implements only the interfaces that actually apply to it.

Production Worker

class ProductionWorker
        implements Workable, Eatable {

 

    @Override
    public void work() {
        System.out.println(
            “I operate a machine on the factory floor.”
        );
    }

 

    @Override
    public void takeLunchBreak() {
        System.out.println(
            “I go to the cafeteria for lunch.”
        );
    }
}
Robot
class Robot implements Workable {

 

    @Override
    public void work() {
        System.out.println(
            “I perform tasks on the assembly line.”
        );
    }
}
Manager
class Manager
        implements Eatable, Manageable {

 

    @Override
    public void takeLunchBreak() {
        System.out.println(
            “I have lunch with a client.”
        );
    }

 

    @Override
    public void attendBoardMeeting() {
        System.out.println(
            “I lead the quarterly board meeting.”
        );
    }
}

What Did We Improve?

No More Unnecessary Exceptions

The Robot class does not have a takeLunchBreak() method.

Nobody can call it accidentally.

The error is impossible because the method does not exist.

Interfaces Are Honest

If a class implements Eatable, it can actually eat.

If it implements Workable, it can actually work.

The contract accurately reflects reality.

Changes Are Isolated

Adding a new method to Manageable affects only managers.

Robots and production workers remain untouched.

Composition Replaces a Monolith

Classes can implement any combination of interfaces.

Just like in real life, a person may fulfill multiple roles at the same time.

Why Is ISP Important?

Easier to Understand

A small interface with one or two methods clearly communicates its purpose.

Seeing:

implements Eatable

immediately tells us what the class can do.

Changes Are Safe and Isolated

When we extend Manageable, only classes implementing that interface are affected.

Unrelated classes remain untouched.

Simpler Testing

If a method depends on:

Workable

we can create a very small mock implementation containing only a single method.

There is no need to implement ten unused methods.

Better Team Collaboration

Narrow interfaces reduce merge conflicts.

Different developers can work independently on different interfaces without constantly interfering with one another.

Consequences of Ignoring ISP

Imagine an interface with 30 methods implemented by 20 classes.

Adding a single new method forces modifications to all 20 classes—even those that will never use it.

Developers often respond by adding:

throw new UnsupportedOperationException();

and moving on, creating traps for future team members.

When Should You Apply ISP?

When Classes Leave Methods Empty or Throw Exceptions

This is the strongest signal of an ISP violation.

The class is essentially saying:

“I have to implement this, but it makes no sense for me.”

When Interface Changes Affect Unrelated Classes

If adding a method forces modifications in classes that will never use that method, the interface is too broad.

In APIs and Libraries

Spring Framework is an excellent example of ISP.

Instead of one large repository interface, Spring Data provides specialized interfaces:

  • CrudRepository
  • PagingAndSortingRepository
  • JpaRepository

Users choose only the level of functionality they actually need.

// Minimal functionality
interface ProductRepository
        extends CrudRepository<Product, Long> {
}

// Extended functionality if needed
interface ProductRepository
        extends JpaRepository<Product, Long> {
}
In E-Commerce Systems

Instead of a single Payment interface containing methods for cards, bank transfers, BLIK, and cryptocurrencies, it is better to create separate interfaces for each payment channel.

A BLIK implementation should not need to know anything about cryptocurrencies.

Memory Trick

Just as a doctor signs a doctor’s contract, not an agreement that also requires repairing cars, each specialist should have a contract tailored to their responsibilities.

A well-designed interface describes one coherent capability.

As a result:

  • classes remain honest about what they can do,
  • contracts remain meaningful,
  • code becomes more predictable,
  • systems become easier and safer to extend.

CATEGORIES:

SOLID Principles

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *

Latest Comments

No comments to show.