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()
andfax()
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 implementscan()
orfax()
- Each class only depends on what it actually uses
- You’re following the Interface Segregation Principle
How to Spot ISP Violations
Symptom | Indicates 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
Feature | Violates 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 Responsibility | Both promote classes/interfaces doing only one thing |
🔍 Separation of Concerns | They avoid overloaded classes or interfaces with too many duties |
💡 Clean & Maintainable Code | They make systems modular, testable, and extendable |
Key Difference Between SRP and ISP
Principle | What it Applies To | Focus | Real-World Example |
---|---|---|---|
🟡 SRP | Classes | Each class should have one reason to change | A ReportGenerator class should only generate, not email or print |
🔵 ISP | Interfaces | Clients should only depend on what they use | A 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:
Layer | SRP Deals With | ISP Deals With |
---|---|---|
Implementation | “What this class is doing” | “What this class is promising to do” |
Behavior Contracts | — | Interfaces |
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
- What is eMMC (Embedded MultiMediaCard) memory ?
- Top 30+ I2C Interview Questions
- Bit Manipulation Interview Questions
- Structure and Union in c
- Little Endian vs. Big Endian: A Complete Guide
- Merge sort algorithm
Special thanks to @mr-raj for contributing to this article on Embedded Pre
Mr. Raj Kumar is a highly experienced Technical Content Engineer with 7 years of dedicated expertise in the intricate field of embedded systems. At Embedded Prep, Raj is at the forefront of creating and curating high-quality technical content designed to educate and empower aspiring and seasoned professionals in the embedded domain.
Throughout his career, Raj has honed a unique skill set that bridges the gap between deep technical understanding and effective communication. His work encompasses a wide range of educational materials, including in-depth tutorials, practical guides, course modules, and insightful articles focused on embedded hardware and software solutions. He possesses a strong grasp of embedded architectures, microcontrollers, real-time operating systems (RTOS), firmware development, and various communication protocols relevant to the embedded industry.
Raj is adept at collaborating closely with subject matter experts, engineers, and instructional designers to ensure the accuracy, completeness, and pedagogical effectiveness of the content. His meticulous attention to detail and commitment to clarity are instrumental in transforming complex embedded concepts into easily digestible and engaging learning experiences. At Embedded Prep, he plays a crucial role in building a robust knowledge base that helps learners master the complexities of embedded technologies.
Leave a Reply