SOLID Principles
Imagine you're designing a system to manage different types of documents. You create a single, all-encompassing IDocument interface that you think will cover every possible action.
// A "fat" interface with multiple responsibilities
public interface IDocument {
void open();
void save();
void print();
void fax();
void spellCheck();
}
This seems fine when you create a RichTextDocument class, which can meaningfully implement all of these methods. But what about a simple ReadOnlyDocument? It can be opened, but it can't be saved or spell-checked. What about a modern CloudDocument? It can be saved, but the concept of "faxing" is completely irrelevant. This is called a "fat" interface, and it violates the Interface Segregation Principle (ISP).
These classes are now forced to deal with methods they don't need.
public class ReadOnlyDocument implements IDocument {
@Override
public void open() { /* ... */ }
@Override
public void save() {
// What do we do here?
throw new UnsupportedOperationException("This document is read-only.");
}
@Override
public void print() { /* ... */ }
@Override
public void fax() {
// This is even worse.
throw new UnsupportedOperationException("Faxing is not supported.");
}
@Override
public void spellCheck() {
throw new UnsupportedOperationException("Cannot modify a read-only document.");
}
}
This is a poor design. It forces classes to implement irrelevant methods, leading to confusing code and potential runtime errors. The IDocument interface is "fat" or "polluted" because it serves too many different client needs at once.
The Interface Segregation Principle addresses this problem directly. It states:
No client should be forced to depend on methods it does not use.
The essence of ISP is to favor many small, client-specific interfaces over one large, general-purpose interface. Instead of designing a "one-size-fits-all" interface, you should design interfaces that are tailored to the specific needs of the client code that will be using them. In many ways, ISP is simply the Single Responsibility Principle applied to interfaces.
The solution is to break our fat IDocument interface down into smaller interfaces, each representing a distinct role or capability.
Step 1: Decompose the "Fat" Interface We identify the different responsibilities and create a small interface for each one.
// Each interface now has a single, cohesive responsibility
public interface Openable {
void open();
}
public interface Saveable {
void save();
}
public interface Printable {
void print();
}
public interface Faxable {
void fax();
}
public interface SpellCheckable {
void spellCheck();
}
Step 2: Implement Only What's Needed Classes now implement only the interfaces that are relevant to them. This is often called a "role interface" approach.
// A RichTextDocument can do almost everything
public class RichTextDocument implements Openable, Saveable, Printable, SpellCheckable {
// Implements all methods meaningfully...
}
// A ReadOnlyDocument has a much smaller, more honest contract
public class ReadOnlyDocument implements Openable, Printable {
@Override
public void open() { /* ... */ }
@Override
public void print() { /* ... */ }
// No need to implement save(), fax(), or spellCheck()!
}
Step 3: Clients Depend on the Smallest Possible Interface
The client code also benefits. A method that only needs to print documents can now depend on the much smaller Printable interface, not the entire IDocument interface.
public class PrintService {
// This service only cares about printing. It doesn't need to know about saving or faxing.
public void printDocument(Printable document) {
document.print();
}
}
The design is now more flexible, and decoupled.
In an interview, you can demonstrate your understanding of ISP by describing how to spot its violation.
UnsupportedOperationException or are just left empty, it's a sure sign that ISP is being violated.instanceof and cast it to a more specific type to access a method it needs, the initial interface was likely too general.ISP is a crucial principle for creating clean, modular, and component-based systems.
UnsupportedOperationException). Segregating the interfaces removes the source of the violation.PrintService is completely unaffected by changes to how documents are saved or spell-checked. This fine-grained decoupling makes the system significantly more flexible and maintainable.