Interface Segregation
A Worker interface has work() and eat().
Cut the interface along who uses it.
Implementers leave methods empty or throw
The picture from life
A restaurant gives a vegetarian guest a veg-only menu, not a thick book with every dish. Nobody has to read or answer for sections they cannot use. A single huge menu forces every guest and every waiter to carry things they never touch.
The problem
What goes wrong when you do not use it.
A Worker interface has work() and eat(). A Robot must implement eat() even though it never eats, so it writes an empty method or throws. Every implementer of the fat interface carries methods it does not need.
- implements
- Implementers leave methods empty or throw
- Callers use only two of the ten methods of an interface
- A change to one method forces recompiling unrelated clients
The idea
How the pattern fixes it.
Cut the interface along who uses it. Workable has work(), Eatable has eat(). A human implements both, a robot only the first. Each client depends only on the small piece it needs.
- implements
Who is who
- Workable
- Tiny interface: work().
- Eatable
- Tiny interface: eat().
- Human, Robot
- Implement only the interfaces that are true for them.
Build it in steps
- 1List the methods of the fat interface.
- 2Group them by which callers use them together.
- 3Make one small interface per group.
- 4Let classes implement only what applies.
Watch it run
Step through the calls. Tap an arrow to jump to it.
run_shift needs only work(), so it asks for Workable and nothing more.
work()The code
Python and Java. Try writing the classes yourself from the diagram first.
class Workable:
def work(self):
raise NotImplementedError
class Eatable:
def eat(self):
raise NotImplementedError
class Human(Workable, Eatable):
def work(self):
return "coding"
def eat(self):
return "lunch"
class Robot(Workable): # no eat() to fake
def work(self):
return "welding"
def run_shift(workers): # needs only work()
return [worker.work() for worker in workers]
def demo():
return run_shift([Human(), Robot()])What you gain
- Implementers carry no dead methods
- Changes ripple to fewer classes
What it costs
- More interface names to manage
- Cutting too finely makes the code scattered
You have already met it in
- Java's Runnable, Comparable and Closeable are tiny interfaces
- Go's one-method interfaces such as Reader and Writer
Often mixed up with
- Single ResponsibilitySRP splits a class by reason to change; ISP splits an interface by who uses it.
Make it stick
Design a Printer / Scanner / Fax machine with interfaces so that a cheap printer is not forced to implement scan() and fax().