Early Access: 87 spots left.

Claim
Low Level DesignInterface SegregationInterface Segregation: Split the OfficeDevice Interface

Course content

Interface Segregation: Split the OfficeDevice Interface

Medium·Tagssolidinterface-segregationrefactoring

Problem Statement

The starter has a single `OfficeDevice` interface that declares three methods: ```java interface OfficeDevice { void print(String document); void scan(); void fax(String number); } ``` Three devices implement it: - `MultiFunctionPrinter` actually supports all three operations. - `BasicPrinter` only prints, but is forced to provide `scan` and `fax` methods that throw `UnsupportedOperationException`. - `OldFaxMachine` only faxes, but is forced to provide `print` and `scan` methods that throw. This violates the Interface Segregation Principle. Two of the three devices are paying for capabilities they do not have. Any caller that holds an `OfficeDevice` reference cannot tell from the type whether `scan` will work or throw, so it has to either guard every call with a try/catch or know the concrete type ahead of time. Both of those are smells. Refactor the design by splitting `OfficeDevice` into three small interfaces, one per capability. The starter already declares them empty: ```java interface Printer { / declare void print(String document) / } interface Scanner { / declare void scan() / } interface Faxer { / declare void fax(String number) / } ``` After your refactor, the design must expose this exact public API: - `Printer` declares `void print(String document)`. - `Scanner` declares `void scan()`. - `Faxer` declares `void fax(String number)`. - `MultiFunctionPrinter` implements `Printer`, `Scanner`, and `Faxer`. - `BasicPrinter` implements `Printer` only and does not expose `scan` or `fax`. - `OldFaxMachine` implements `Faxer` only and does not expose `print` or `scan`. The `OfficeDevice` interface and the throwing stub methods on `BasicPrinter` and `OldFaxMachine` should be removed. After the refactor, every method that exists on a device is one the device actually supports. The validator runs five checks: 1. `MultiFunctionPrinter` supports all three capabilities. The validator casts it to each of `Printer`, `Scanner`, and `Faxer` and confirms every method runs without throwing. 2. `BasicPrinter` implements `Printer` and only `Printer`. Reflection confirms `Printer.class.isAssignableFrom(BasicPrinter.class)` is true while the same check for `Scanner` and `Faxer` returns false. The print method itself works correctly. 3. `OldFaxMachine` implements `Faxer` and only `Faxer`. Same shape as check 2, mirrored. 4. The three small interfaces declare exactly the right methods: `Printer.print(String)`, `Scanner.scan()`, `Faxer.fax(String)`. 5. The unused-method check, which is the strict ISP test. The validator uses reflection to confirm that `BasicPrinter` exposes neither `scan()` nor `fax(String)` through any inheritance path, and that `OldFaxMachine` exposes neither `print(String)` nor `scan()`. This forces the actual structural change. If you keep `BasicPrinter implements OfficeDevice`, the inherited `scan` and `fax` methods are still on its public API, and this check fails. All five checks fail in the smelly starter. Each requires the actual refactor.

Examples

Example 1
Input
Printer p = new BasicPrinter(); p.print("report.pdf")
Output
"(prints \"report.pdf\")"
Why
BasicPrinter only implements Printer. Callers that need printing capability ask for a Printer reference.
Example 2
Input
Scanner s = (Scanner) new BasicPrinter()
Output
"compile error after refactor (or ClassCastException with current cast syntax)"
Why
BasicPrinter does not implement Scanner. Callers that need scanning ask for a Scanner reference, which a BasicPrinter cannot satisfy. The type system tells them so at the cast site.
Example 3
Input
MultiFunctionPrinter mfp = new MultiFunctionPrinter(); mfp.print("doc"); mfp.scan(); mfp.fax("555-1234")
Output
"(prints, scans, and faxes)"
Why
An MFP truly supports all three capabilities, so it implements all three interfaces.

Constraints

  • Refactor must produce three small interfaces named Printer, Scanner, Faxer with one method each.
  • MultiFunctionPrinter must implement Printer, Scanner, and Faxer.
  • BasicPrinter must implement Printer only.
  • OldFaxMachine must implement Faxer only.
  • BasicPrinter must not expose a scan() or fax(String) method through any inheritance path.
  • OldFaxMachine must not expose a print(String) or scan() method through any inheritance path.

Hints

Stuck? Reveal a nudge toward the right pattern, one step at a time.

Hint 1
Add the missing method to each small interface first. Printer needs `void print(String document)`, Scanner needs `void scan()`, Faxer needs `void fax(String number)`. They are empty in the starter on purpose.
Hint 2
Update the device classes one at a time. MultiFunctionPrinter genuinely supports all three, so it implements Printer, Scanner, and Faxer. The bodies of its print, scan, and fax stay the same.
Hint 3
BasicPrinter only prints. Change its declaration to `class BasicPrinter implements Printer`. Delete the scan and fax methods entirely, including their throw stubs.
Hint 4
OldFaxMachine only faxes. Change its declaration to `class OldFaxMachine implements Faxer`. Delete the print and scan methods entirely.
Hint 5
Once no class implements OfficeDevice, you can delete the OfficeDevice interface. The validator does not require this, but it removes dead code from your design.
Hint 6
The strictest validator check uses Class.getMethod to confirm the unused methods are gone from the inheritance chain. If you leave `implements OfficeDevice` on a device, the inherited methods are still on its public API and the check fails.