The Interface Segregation Principle (ISP) is one of the SOLID principles of object-oriented programming and design. It emphasizes creating focused, lean interfaces that cater only to the specific needs of a client, rather than forcing clients to implement methods they don’t use.
The principle states:
“Clients should not be forced to depend on methods they do not use.”
In simpler terms, large, monolithic interfaces should be broken into smaller, more specific interfaces that only include methods relevant to their users. This prevents unnecessary dependencies and promotes cleaner, modular design.
Let us discuss this in detail with an example.
Printer Example
Initial Design (Violates ISP)
Imagine designing a system for different types of printers. A base Printer interface might look like this:
class Printer: def print_document(self, document): pass def scan_document(self, document): pass def fax_document(self, document): pass
A basic printer that only supports printing is now forced to implement scan_document and fax_document, even though it doesn’t support these functionalities:
class BasicPrinter(Printer): def print_document(self, document): print(f"Printing document: {document}") def scan_document(self, document): raise NotImplementedError("This printer does not support scanning") def fax_document(self, document): raise NotImplementedError("This printer does not support faxing")
This design violates ISP because the BasicPrinter class is forced to depend on methods (scan_document and fax_document) it doesn’t use or support.
Improved Design
To adhere to ISP, we split the Printer interface into smaller, more specific interfaces:
class Printer: def print_document(self, document): pass class Scanner: def scan_document(self, document): pass class Fax: def fax_document(self, document): pass
Now, each type of printer can implement only the interfaces it needs:
class BasicPrinter(Printer): def print_document(self, document): print(f"Printing document: {document}") class MultiFunctionPrinter(Printer, Scanner, Fax): def print_document(self, document): print(f"Printing document: {document}") def scan_document(self, document): print(f"Scanning document: {document}") def fax_document(self, document): print(f"Faxing document: {document}")
Class diagram using mermaid to get more understanding
classDiagram class Printer { +print_document(document): void } class Scanner { +scan_document(document): void } class Fax { +fax_document(document): void } class BasicPrinter { +print_document(document): void } class MultiFunctionPrinter { +print_document(document): void +scan_document(document): void +fax_document(document): void } BasicPrinter --> Printer MultiFunctionPrinter --> Printer MultiFunctionPrinter --> Scanner MultiFunctionPrinter --> Fax
Conclusion
The Interface Segregation Principle ensures clean, modular, and reusable code by dividing large interfaces into smaller, more focused ones. This not only prevents unnecessary dependencies but also enhances the scalability and maintainability of your system.
By applying ISP, developers can create systems where components do exactly what they’re supposed to—no more, no less—leading to robust, future-proof software designs.