Interface Segregation Principle (ISP)
The Interface Segregation Principle (ISP) is one of the SOLID principles in object-oriented design.
It states that a class should not be forced to implement interfaces it doesn't use. Instead, it's better to have multiple, smaller, and more specific interfaces, so that classes only implement the methods that are relevant to them.
Key Concepts:
Cohesion: Interfaces should be focused on specific functionalities.
No Unnecessary Methods: A class should not be burdened with implementing methods it doesn't need.
Smaller Interfaces: Breaking down a large interface into smaller, more specific ones that only define the methods relevant to a particular group of clients.
Let's create a TypeScript example that adheres to the Interface Segregation Principle (ISP) in the context of printers.
Scenario:
Imagine you have different types of printers, such as InkjetPrinter, LaserPrinter, and MultiFunctionPrinter. Each printer type has different capabilities, like printing, scanning, and document sharing. However, not all printers support all these functions.
Without ISP (Bad Example):
In a situation where ISP is violated, you might have a single interface that defines all possible printer functions:
interface Printer {
print(document: string): void;
scan(document: string): void;
shareDocument(document: string, destination: string): void;
}
class InkjetPrinter implements Printer {
print(document: string): void {
console.log(`InkjetPrinter printing: ${document}`);
}
scan(document: string): void {
throw new Error("InkjetPrinter does not support scanning");
}
shareDocument(document: string, destination: string): void {
throw new Error("InkjetPrinter does not support document sharing");
}
}
class LaserPrinter implements Printer {
print(document: string): void {
console.log(`LaserPrinter printing: ${document}`);
}
scan(document: string): void {
throw new Error("LaserPrinter does not support scanning");
}
shareDocument(document: string, destination: string): void{
throw new Error("LaserPrinter does not support document sharing");
}
}
class MultiFunctionPrinter implements Printer {
print(document: string): void {
console.log(`MultiFunctionPrinter printing: ${document}`);
}
scan(document: string): void {
console.log(`MultiFunctionPrinter scanning: ${document}`);
}
shareDocument(document: string, destination: string): void {
console.log(`MultiFunctionPrinter document sharing: ${document}`);
}
}
In this example, InkjetPrinter and LaserPrinter are forced to implement scan and shareDocument methods even though they don't support these features. This violates the Interface Segregation Principle.
Refactoring with ISP:
To adhere to ISP, we should create smaller, more specific interfaces:
interface Printer {
print(document: string): void;
}
interface Scanner {
scan(document: string): void;
}
interface DocumentSharer {
shareDocument(document: string, destination: string): void;
}
Now, each printer type can implement only the interfaces relevant to its capabilities:
class InkjetPrinter implements Printer {
print(document: string): void {
console.log(`InkjetPrinter printing: ${document}`);
}
}
class LaserPrinter implements Printer {
print(document: string): void {
console.log(`LaserPrinter printing: ${document}`);
}
}
class MultiFunctionPrinter implements Printer, Scanner, DocumentSharer {
print(document: string): void {
console.log(`MultiFunctionPrinter printing: ${document}`);
}
scan(document: string): void {
console.log(`MultiFunctionPrinter scanning: ${document}`);
}
shareDocument(document: string, destination: string): void {
console.log(
`MultiFunctionPrinter sharing document: ${document} to ${destination}`
);
}
}
Explanation:
InkjetPrinter: Implements only the Printer interface, as it only needs to print documents.
LaserPrinter: Also implements only the Printer interface, focusing on printing.
MultiFunctionPrinter: Implements Printer, Scanner, and DocumentSharer, covering all the functionalities required for a modern office environment, including the ability to share documents securely.
Usage Example:
//Usage Example:
const inkjet = new InkjetPrinter();
inkjet.print("Report.pdf");
const laser = new LaserPrinter();
laser.print("Invoice.pdf");
const mfp = new MultiFunctionPrinter();
mfp.print("Contract.pdf");
mfp.scan("Contract.pdf");
mfp.shareDocument("Contract.pdf", "https://XYZcloud.service.com/upload");
This approach ensures that each printer type only implements the methods it needs, adhering to the Interface Segregation Principle.
The GITHUB LINK(ISP) contains the code samples for reference.
Conclusion
The Interface Segregation Principle (ISP) emphasises that no client should be forced to depend on methods it does not use. By designing smaller, more specific interfaces, we ensure that each class or component implements only what it needs, leading to more maintainable, flexible, and understandable code. This approach reduces unnecessary dependencies, avoids bloated interfaces, and enhances the clarity of each component's role within the system. Adhering to ISP helps in creating modular and scalable systems where changes are easier to manage, and components are more reusable and testable.