Restructuring the code to follow ISP
In the previous lesson, we saw how a bloated Printer interface violated the Interface Segregation Principle (ISP) by forcing classes to implement methods they didn’t need. To solve this, we will refactor the code to create smaller, more specific interfaces that only define the methods needed by each type of printer.
Step 1: Break the Large Interface into Smaller Interfaces
Instead of having a single large Printer interface, we can split it into more focused interfaces. Each interface will represent a specific functionality, allowing classes to implement only what they need.
// Interface for basic printing functionality
public interface Printer {
void printDocument(String document);
}
// Interface for scanning functionality
public interface Scanner {
void scanDocument(String document);
}
// Interface for faxing functionality
public interface Fax {
void faxDocument(String document);
}
// Interface for stapling functionality
public interface Stapler {
void stapleDocument(String document);
}
Now, we have four separate interfaces: Printer for printing, Scanner for scanning, Fax for faxing, and Stapler for stapling. This way, classes only need to implement the interfaces they require.
Step 2: Implementing BasicPrinter
Let’s refactor the BasicPrinter class so it only implements the Printer interface. Since the BasicPrinter only needs to print documents, we no longer have to worry about unnecessary methods like scanning or faxing.
public class BasicPrinter implements Printer {
@Override
public void printDocument(String document) {
System.out.println("Printing document: " + document);
}
}
The BasicPrinter class is now clean and focused. It doesn’t need to implement any methods it doesn’t use, which simplifies the code and avoids throwing exceptions for unsupported operations.
Step 3: Implementing AdvancedPrinter
For an AdvancedPrinter that supports all functionalities (printing, scanning, faxing, and stapling), we can implement multiple interfaces. This allows the class to inherit only the behaviors it needs, without being forced to implement everything from a single large interface.
public class AdvancedPrinter implements Printer, Scanner, Fax, Stapler {
@Override
public void printDocument(String document) {
System.out.println("Printing document: " + document);
}
@Override
public void scanDocument(String document) {
System.out.println("Scanning document: " + document);
}
@Override
public void faxDocument(String document) {
System.out.println("Faxing document: " + document);
}
@Override
public void stapleDocument(String document) {
System.out.println("Stapling document: " + document);
}
}
The AdvancedPrinter now implements all the relevant interfaces (Printer, Scanner, Fax, and Stapler). This allows the class to handle each task while keeping the design clean and modular.
Step 4: Test the New Design
Here’s a quick test to see how the new design works with different types of printers.
// Printer interface for basic printing functionality
interface Printer {
void printDocument(String document);
}
// Scanner interface for scanning functionality
interface Scanner {
void scanDocument(String document);
}
// Fax interface for faxing functionality
interface Fax {
void faxDocument(String document);
}
// Stapler interface for stapling functionality
interface Stapler {
void stapleDocument(String document);
}
// BasicPrinter class that only implements Printer
class BasicPrinter implements Printer {
@Override
public void printDocument(String document) {
System.out.println("Printing document: " + document);
}
}
// AdvancedPrinter class that implements multiple interfaces
class AdvancedPrinter implements Printer, Scanner, Fax, Stapler {
@Override
public void printDocument(String document) {
System.out.println("Printing document: " + document);
}
@Override
public void scanDocument(String document) {
System.out.println("Scanning document: " + document);
}
@Override
public void faxDocument(String document) {
System.out.println("Faxing document: " + document);
}
@Override
public void stapleDocument(String document) {
System.out.println("Stapling document: " + document);
}
}
// Main class to test the new design
public class Solution {
public static void main(String[] args) {
// Testing BasicPrinter (only supports printing)
Printer basicPrinter = new BasicPrinter();
basicPrinter.printDocument("Basic Document");
// Testing AdvancedPrinter (supports all functions)
AdvancedPrinter advancedPrinter = new AdvancedPrinter();
advancedPrinter.printDocument("Advanced Document");
advancedPrinter.scanDocument("Advanced Document");
advancedPrinter.faxDocument("Advanced Document");
advancedPrinter.stapleDocument("Advanced Document");
}
}
Why This Follows ISP?
By restructuring the code:
- Smaller, Focused Interfaces: Each interface now represents a specific task (printing, scanning, faxing, or stapling). This allows classes to implement only what they need, following the Interface Segregation Principle.
- More Flexible Design: Classes like
BasicPrinteronly need to implement thePrinterinterface, avoiding unnecessary methods. Meanwhile,AdvancedPrintercan implement multiple interfaces to support a range of functionalities. - Cleaner, Easier to Maintain Code: The code is easier to understand and maintain since each class focuses on the exact responsibilities it needs to fulfill.
🤖 Don't fully get this? Learn it with Claude
Stuck on Restructuring the code to follow ISP? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Restructuring the code to follow ISP** (OO & Low-Level Design) and want to truly understand it. Explain Restructuring the code to follow ISP from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Restructuring the code to follow ISP** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Restructuring the code to follow ISP** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Restructuring the code to follow ISP** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.