I – Interface Segregation Principle (ISP) | Master CPP Design Patterns 2025

The Interface Segregation Principle (ISP) is one of the five SOLID principles of object-oriented programming. It states:

Clients should not be forced to depend on interfaces they do not use.

In simpler terms, this principle encourages splitting large, unwieldy interfaces into smaller, more specific ones so that classes only need to implement the methods they actually use.

Why Interface Segregation Principle Matters

When designing systems—especially in embedded software and large-scale applications—overly fat interfaces can lead to:

  • Unnecessary code coupling
  • Wasted resources in constrained systems
  • Fragile software that’s hard to maintain or extend

By adhering to ISP, you build cleaner, modular, and more maintainable codebases that are easier to test and evolve over time.

What does this mean (simple version)?

Imagine an interface (abstract class) with 10 methods.
You only need 2 of them — but you still have to override all 10, even the ones you don’t use. 😖

That’s bad design.

With ISP, you split that big interface into smaller purpose-specific ones so that:

  • Classes only implement what they really need
  • They’re not bloated with useless methods

Real-Life Analogy

Let’s say you go to a restaurant.

If you only want coffee ☕, but they hand you a menu with 100 items, and ask you to order from every category — that’s overwhelming, right?

Wouldn’t it be better if they had a “Drinks” menu separate from the “Meals” and “Desserts”?

That’s Interface Segregation — keep things small, focused, and relevant.

Bad Example: Violates ISP

Let’s create a fat interface:

class IMachine {
public:
    virtual void print() = 0;
    virtual void scan() = 0;
    virtual void fax() = 0;
};

Now you want to create a basic printer:

class OldPrinter : public IMachine {
public:
    void print() override {
        cout << "Printing..." << endl;
    }

    void scan() override {
        // Not supported
        throw runtime_error("Scan not supported");
    }

    void fax() override {
        // Not supported
        throw runtime_error("Fax not supported");
    }
};

🔴 Problem:

  • You’re forced to implement scan() and fax() even if your class doesn’t need them.
  • You end up writing empty methods or throwing exceptions — that’s bad.

Good Design: Follows ISP

Split the large interface into smaller ones:

class IPrinter {
public:
    virtual void print() = 0;
};

class IScanner {
public:
    virtual void scan() = 0;
};

class IFax {
public:
    virtual void fax() = 0;
};

Now your classes only implement what they actually need:

class BasicPrinter : public IPrinter {
public:
    void print() override {
        cout << "Basic printing..." << endl;
    }
};

class MultiFunctionPrinter : public IPrinter, public IScanner, public IFax {
public:
    void print() override {
        cout << "Printing..." << endl;
    }

    void scan() override {
        cout << "Scanning..." << endl;
    }

    void fax() override {
        cout << "Faxing..." << endl;
    }
};

Now:

  • BasicPrinter isn’t forced to implement scan() or fax()
  • Each class only depends on what it actually uses
  • You’re following the Interface Segregation Principle

How to Spot ISP Violations

SymptomIndicates ISP Violation?
Class implements unused methods✅ Yes
You throw exceptions in unused methods✅ Yes
Interface has too many responsibilities✅ Yes
Difficult to test one specific feature✅ Yes

Tips to Apply ISP

  • 🔹 If your interface has more than 3–4 methods, check if it can be split.
  • 🔹 Group methods by behavior (e.g., Print, Scan, Fax = 3 separate concerns).
  • 🔹 Don’t overdo it — small, cohesive interfaces are ideal.
  • 🔹 Use multiple inheritance of interfaces for combining features in C++.

ISP Mini Project Idea

Let’s say you’re building a Vehicle system.

Bad:

class IVehicle {
public:
    virtual void fly() = 0;
    virtual void drive() = 0;
    virtual void sail() = 0;
};

Now what if you just want to implement a car? 🚗

Instead:

class IDriveable {
public:
    virtual void drive() = 0;
};

class IFlyable {
public:
    virtual void fly() = 0;
};

class ISailable {
public:
    virtual void sail() = 0;
};

Now a car only implements IDriveable, and a boat only implements ISailable.
No more useless methods!

Summary of ISP

FeatureViolates ISP ❌Follows ISP ✅
Big interfaces
Empty/unimplemented methods
Multiple small interfaces
Clear class responsibilities

Common Ground Between SRP and ISP

They Both Want…Description
🎯 Focused ResponsibilityBoth promote classes/interfaces doing only one thing
🔍 Separation of ConcernsThey avoid overloaded classes or interfaces with too many duties
💡 Clean & Maintainable CodeThey make systems modular, testable, and extendable

Key Difference Between SRP and ISP

PrincipleWhat it Applies ToFocusReal-World Example
🟡 SRPClassesEach class should have one reason to changeA ReportGenerator class should only generate, not email or print
🔵 ISPInterfacesClients should only depend on what they useA Printer class shouldn’t be forced to implement scan()

In Summary:

➕ SRP:

“Don’t overload classes with multiple jobs.”

➕ ISP:

“Don’t overload interfaces with methods clients won’t use.”

Think of it like:

LayerSRP Deals WithISP Deals With
Implementation“What this class is doing”“What this class is promising to do”
Behavior ContractsInterfaces

Side-by-Side Quick Example

// SRP
class ReportGenerator {
public:
    void generate() { /* ... */ }
};
// Bad: also sends emails or logs things

// ISP
class IPrinter {
public:
    virtual void print() = 0;
};

class IScanner {
public:
    virtual void scan() = 0;
};
// Good: only implement what you need

Final Takeaway:

🔧 SRP keeps classes focused.
🔌 ISP keeps interfaces focused.

They work hand-in-hand to make your code clean, modular, and friendly to change.

You can also Visit other tutorials of Embedded Prep 

Special thanks to @mr-raj for contributing to this article on Embedded Pre

Leave a Reply

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