design·lab

I · SOLID

Interface Segregation

No client should be forced to depend on methods it doesn't use — split fat interfaces into small, role-specific ones.

✗ The problem

One fat interface, forced stubs

Every machine must implement all three methods — even a plain printer that can't scan or fax.

interface IMachine {
  print(doc);
  scan(doc);
  fax(doc);
}

class OldPrinter implements IMachine {
  print(doc) { /* real work */ }
  scan(doc)  { throw new Error('not supported'); }
  fax(doc)   { throw new Error('not supported'); }
}
IMachine
print · scan · fax
↓ forced to implement all
OldPrinter
scan() / fax() → throw
Clients depend on methods they don't use. A change to fax() forces recompile/retest of printers that never fax.
✓ Refactor

Split into small role interfaces

Each class implements only the roles it actually needs.

interface Printer  { print(doc); }
interface Scanner  { scan(doc); }
interface Fax      { fax(doc); }

class OldPrinter implements Printer { … }

class MultiFunctionDevice
    implements Printer, Scanner, Fax { … }
Printer
print()
Scanner
scan()
Fax
fax()
↑ implemented by only who needs them
OldPrinter
Printer
MultiFunctionDevice
Printer · Scanner · Fax
Small interfaces ← only the classes that actually need them. No more stub methods that throw.
✓ See it live

Fat interface vs segregated

Toggle the design, then call a method on each device — watch which calls are even possible.

OldPrinter
print · scan · fax

MultiFunctionDevice
print · scan · fax

Fat mode: OldPrinter carries scan()/fax() it can't fulfil — calling them throws.

✓ Takeaway

Many small interfaces beat one fat one

  • Rule: clients should depend only on the methods they actually use.
  • Smell: stub methods that throw or do nothing — a sign the interface is too fat.
  • Related: closely tied to SRP — an interface should have one reason to change, just like a class.
  • Benefit: smaller, focused contracts are easier to implement, mock, and evolve.