Interface Segregation Principle
The Interface Segregation Principle (ISP) states that clients should not be forced to depend on interfaces they don’t use. Instead of one fat interface, many small, specific interfaces are preferred.
This principle helps prevent the creation of “fat” or “bloated” interfaces that force implementing classes to provide empty implementations for methods they don’t need.
Understanding the Principle
Section titled “Understanding the Principle”The Interface Segregation Principle ensures that:
- Interfaces are focused - Each interface has a single, well-defined purpose
- No forced implementations - Classes don’t implement methods they don’t need
- Better cohesion - Related methods are grouped together
- Loose coupling - Clients depend only on what they actually use
In simple terms: Don’t force a class to implement methods it doesn’t need!
Example 1: Worker Interface Problem
Section titled “Example 1: Worker Interface Problem”Consider a system with different types of workers. Let’s see what happens when we create a fat interface.
Violating ISP (Bad Approach)
Section titled “Violating ISP (Bad Approach)”from abc import ABC, abstractmethod
class Worker(ABC): """❌ Fat interface - violates ISP""" @abstractmethod def work(self): """All workers can work""" pass
@abstractmethod def eat(self): """❌ Problem: Not all workers eat!""" pass
@abstractmethod def sleep(self): """❌ Problem: Not all workers sleep!""" pass
class HumanWorker(Worker): """Human worker - can do all three""" def work(self): print("Human is working...")
def eat(self): print("Human is eating...")
def sleep(self): print("Human is sleeping...")
class RobotWorker(Worker): """❌ Robot worker - forced to implement methods it doesn't need!""" def work(self): print("Robot is working...")
def eat(self): """❌ Robots don't eat! Empty implementation""" raise NotImplementedError("Robots don't eat!")
def sleep(self): """❌ Robots don't sleep! Empty implementation""" raise NotImplementedError("Robots don't sleep!")
# Usagehuman = HumanWorker()human.work() # ✅ Workshuman.eat() # ✅ Workshuman.sleep() # ✅ Works
robot = RobotWorker()robot.work() # ✅ Worksrobot.eat() # ❌ Breaks! Robots don't eatrobot.sleep() # ❌ Breaks! Robots don't sleep// ❌ Fat interface - violates ISPpublic interface Worker { // All workers can work void work();
// ❌ Problem: Not all workers eat! void eat();
// ❌ Problem: Not all workers sleep! void sleep();}
// Human worker - can do all threepublic class HumanWorker implements Worker { @Override public void work() { System.out.println("Human is working..."); }
@Override public void eat() { System.out.println("Human is eating..."); }
@Override public void sleep() { System.out.println("Human is sleeping..."); }}
// ❌ Robot worker - forced to implement methods it doesn't need!public class RobotWorker implements Worker { @Override public void work() { System.out.println("Robot is working..."); }
// ❌ Robots don't eat! Empty implementation @Override public void eat() { throw new UnsupportedOperationException("Robots don't eat!"); }
// ❌ Robots don't sleep! Empty implementation @Override public void sleep() { throw new UnsupportedOperationException("Robots don't sleep!"); }}
// Usagepublic class Main { public static void main(String[] args) { HumanWorker human = new HumanWorker(); human.work(); // ✅ Works human.eat(); // ✅ Works human.sleep(); // ✅ Works
RobotWorker robot = new RobotWorker(); robot.work(); // ✅ Works robot.eat(); // ❌ Breaks! Robots don't eat robot.sleep(); // ❌ Breaks! Robots don't sleep }}Following ISP (Good Approach)
Section titled “Following ISP (Good Approach)”from abc import ABC, abstractmethod
class Workable(ABC): """Focused interface - only work capability""" @abstractmethod def work(self): """All workers can work""" pass
class Eatable(ABC): """Focused interface - only eating capability""" @abstractmethod def eat(self): """Only workers that eat implement this""" pass
class Sleepable(ABC): """Focused interface - only sleeping capability""" @abstractmethod def sleep(self): """Only workers that sleep implement this""" pass
class HumanWorker(Workable, Eatable, Sleepable): """Human implements all interfaces it needs""" def work(self): print("Human is working...")
def eat(self): print("Human is eating...")
def sleep(self): print("Human is sleeping...")
class RobotWorker(Workable): """Robot only implements what it needs - work""" def work(self): print("Robot is working...") # No eat() or sleep() - correct!
# Usage - Clean and focused!human = HumanWorker()human.work() # ✅ Workshuman.eat() # ✅ Workshuman.sleep() # ✅ Works
robot = RobotWorker()robot.work() # ✅ Works# robot.eat() # ✅ Type error - prevents calling methods robot doesn't have# robot.sleep() # ✅ Type error - prevents calling methods robot doesn't have// Focused interface - only work capabilitypublic interface Workable { // All workers can work void work();}
// Focused interface - only eating capabilitypublic interface Eatable { // Only workers that eat implement this void eat();}
// Focused interface - only sleeping capabilitypublic interface Sleepable { // Only workers that sleep implement this void sleep();}
// Human implements all interfaces it needspublic class HumanWorker implements Workable, Eatable, Sleepable { @Override public void work() { System.out.println("Human is working..."); }
@Override public void eat() { System.out.println("Human is eating..."); }
@Override public void sleep() { System.out.println("Human is sleeping..."); }}
// Robot only implements what it needs - workpublic class RobotWorker implements Workable { @Override public void work() { System.out.println("Robot is working..."); } // No eat() or sleep() - correct!}
// Usage - Clean and focused!public class Main { public static void main(String[] args) { HumanWorker human = new HumanWorker(); human.work(); // ✅ Works human.eat(); // ✅ Works human.sleep(); // ✅ Works
RobotWorker robot = new RobotWorker(); robot.work(); // ✅ Works // robot.eat(); // ✅ Compile error - prevents calling methods robot doesn't have // robot.sleep(); // ✅ Compile error - prevents calling methods robot doesn't have }}Why this follows ISP:
- Each interface has a single, focused responsibility
- Classes implement only the interfaces they need
- No empty implementations or exceptions
- Changes to one interface don’t affect unrelated classes
Example 2: Document Management System
Section titled “Example 2: Document Management System”Consider a document management system with different types of devices that can interact with documents.
Violating ISP (Bad Approach)
Section titled “Violating ISP (Bad Approach)”from abc import ABC, abstractmethod
class Device(ABC): """❌ Fat interface - violates ISP""" @abstractmethod def print(self, document: str): """Print a document""" pass
@abstractmethod def scan(self) -> str: """Scan a document""" pass
@abstractmethod def fax(self, document: str): """Fax a document""" pass
@abstractmethod def email(self, document: str): """Email a document""" pass
class Printer(Device): """Printer - can print, but not scan/fax/email""" def print(self, document: str): print(f"Printing: {document}")
def scan(self) -> str: """❌ Printer can't scan!""" raise NotImplementedError("This printer cannot scan!")
def fax(self, document: str): """❌ Printer can't fax!""" raise NotImplementedError("This printer cannot fax!")
def email(self, document: str): """❌ Printer can't email!""" raise NotImplementedError("This printer cannot email!")
class Scanner(Device): """Scanner - can scan, but not print/fax/email""" def print(self, document: str): """❌ Scanner can't print!""" raise NotImplementedError("This scanner cannot print!")
def scan(self) -> str: return f"Scanned: {document}"
def fax(self, document: str): """❌ Scanner can't fax!""" raise NotImplementedError("This scanner cannot fax!")
def email(self, document: str): """❌ Scanner can't email!""" raise NotImplementedError("This scanner cannot email!")
# Usage - Lots of exceptions!printer = Printer()printer.print("doc.pdf") # ✅ Worksprinter.scan() # ❌ Raises exception!// ❌ Fat interface - violates ISPpublic interface Device { // Print a document void print(String document);
// Scan a document String scan();
// Fax a document void fax(String document);
// Email a document void email(String document);}
// Printer - can print, but not scan/fax/emailpublic class Printer implements Device { @Override public void print(String document) { System.out.println("Printing: " + document); }
// ❌ Printer can't scan! @Override public String scan() { throw new UnsupportedOperationException("This printer cannot scan!"); }
// ❌ Printer can't fax! @Override public void fax(String document) { throw new UnsupportedOperationException("This printer cannot fax!"); }
// ❌ Printer can't email! @Override public void email(String document) { throw new UnsupportedOperationException("This printer cannot email!"); }}
// Scanner - can scan, but not print/fax/emailpublic class Scanner implements Device { // ❌ Scanner can't print! @Override public void print(String document) { throw new UnsupportedOperationException("This scanner cannot print!"); }
@Override public String scan() { return "Scanned: " + document; }
// ❌ Scanner can't fax! @Override public void fax(String document) { throw new UnsupportedOperationException("This scanner cannot fax!"); }
// ❌ Scanner can't email! @Override public void email(String document) { throw new UnsupportedOperationException("This scanner cannot email!"); }}
// Usage - Lots of exceptions!public class Main { public static void main(String[] args) { Printer printer = new Printer(); printer.print("doc.pdf"); // ✅ Works printer.scan(); // ❌ Throws exception! }}Following ISP (Good Approach)
Section titled “Following ISP (Good Approach)”from abc import ABC, abstractmethod
class Printable(ABC): """Focused interface - printing capability""" @abstractmethod def print(self, document: str): """Print a document""" pass
class Scannable(ABC): """Focused interface - scanning capability""" @abstractmethod def scan(self) -> str: """Scan a document""" pass
class Faxable(ABC): """Focused interface - faxing capability""" @abstractmethod def fax(self, document: str): """Fax a document""" pass
class Emailable(ABC): """Focused interface - emailing capability""" @abstractmethod def email(self, document: str): """Email a document""" pass
class Printer(Printable): """Printer only implements what it can do""" def print(self, document: str): print(f"Printing: {document}")
class Scanner(Scannable): """Scanner only implements what it can do""" def scan(self) -> str: return "Scanned document content"
class MultiFunctionDevice(Printable, Scannable, Faxable, Emailable): """Multi-function device implements all interfaces it supports""" def print(self, document: str): print(f"Printing: {document}")
def scan(self) -> str: return "Scanned document content"
def fax(self, document: str): print(f"Faxing: {document}")
def email(self, document: str): print(f"Emailing: {document}")
# Helper functions that work with specific interfacesdef print_document(device: Printable, document: str): """Works with any Printable device""" device.print(document)
def scan_document(device: Scannable) -> str: """Works with any Scannable device""" return device.scan()
# Usage - Clean and type-safe!printer = Printer()scanner = Scanner()multi_device = MultiFunctionDevice()
print_document(printer, "doc.pdf") # ✅ Worksprint_document(multi_device, "doc.pdf") # ✅ Works# print_document(scanner, "doc.pdf") # ✅ Type error - scanner can't print
scan_document(scanner) # ✅ Worksscan_document(multi_device) # ✅ Works# scan_document(printer) # ✅ Type error - printer can't scan// Focused interface - printing capabilitypublic interface Printable { // Print a document void print(String document);}
// Focused interface - scanning capabilitypublic interface Scannable { // Scan a document String scan();}
// Focused interface - faxing capabilitypublic interface Faxable { // Fax a document void fax(String document);}
// Focused interface - emailing capabilitypublic interface Emailable { // Email a document void email(String document);}
// Printer only implements what it can dopublic class Printer implements Printable { @Override public void print(String document) { System.out.println("Printing: " + document); }}
// Scanner only implements what it can dopublic class Scanner implements Scannable { @Override public String scan() { return "Scanned document content"; }}
// Multi-function device implements all interfaces it supportspublic class MultiFunctionDevice implements Printable, Scannable, Faxable, Emailable { @Override public void print(String document) { System.out.println("Printing: " + document); }
@Override public String scan() { return "Scanned document content"; }
@Override public void fax(String document) { System.out.println("Faxing: " + document); }
@Override public void email(String document) { System.out.println("Emailing: " + document); }}
// Helper functions that work with specific interfacespublic class DeviceService { // Works with any Printable device public static void printDocument(Printable device, String document) { device.print(document); }
// Works with any Scannable device public static String scanDocument(Scannable device) { return device.scan(); }
public static void main(String[] args) { // Usage - Clean and type-safe! Printer printer = new Printer(); Scanner scanner = new Scanner(); MultiFunctionDevice multiDevice = new MultiFunctionDevice();
printDocument(printer, "doc.pdf"); // ✅ Works printDocument(multiDevice, "doc.pdf"); // ✅ Works // printDocument(scanner, "doc.pdf"); // ✅ Compile error - scanner can't print
scanDocument(scanner); // ✅ Works scanDocument(multiDevice); // ✅ Works // scanDocument(printer); // ✅ Compile error - printer can't scan }}Why this follows ISP:
- Each interface represents a single capability
- Devices implement only the interfaces they support
- No empty implementations or exceptions
- Type system prevents calling unsupported methods
- Easy to add new capabilities without affecting existing devices
Benefits of Following ISP
Section titled “Benefits of Following ISP”Key Takeaways
Section titled “Key Takeaways”Remember: The Interface Segregation Principle ensures that classes only depend on methods they actually use, leading to cleaner and more maintainable code! 🎯