Interface Segregation Principle
You'll learn to
- -Split a fat interface into smaller, role-specific ones so implementers never depend on methods they do not use
- -Balance ISP against over-fragmenting interfaces into too many tiny pieces
The Interface Segregation Principle (ISP) states that no client should be forced to depend on methods it does not use. In practice: a single "fat" interface that bundles unrelated capabilities forces every implementer to either implement methods that make no sense for it, or throw exceptions from them - which is the interface-level cousin of the Liskov Substitution violations from the previous chapter.
The Classic Fat-Interface Trap
A Worker interface with work() and eat() methods looks reasonable for a HumanWorker, but forces a RobotWorker to implement eat() with an empty body or an exception - RobotWorker has been forced to depend on a method it genuinely has no use for. The fix is splitting Worker into Workable (work()) and Eatable (eat()), so HumanWorker implements both and RobotWorker implements only Workable, with no dead or exception-throwing method anywhere.
# Before: RobotWorker is forced to implement eat(), which makes no sense
class Worker(ABC):
@abstractmethod
def work(self) -> None: ...
@abstractmethod
def eat(self) -> None: ...
# After: capabilities are separate, implemented only where they apply
class Workable(ABC):
@abstractmethod
def work(self) -> None: ...
class Eatable(ABC):
@abstractmethod
def eat(self) -> None: ...
class HumanWorker(Workable, Eatable): ...
class RobotWorker(Workable): ... # never forced to implement eat()Do Not Fragment Past the Point of Usefulness
ISP has the same overcorrection risk as SRP: splitting an interface into one method per interface "just in case" produces a maze of tiny types that adds ceremony without adding clarity. The right granularity groups methods that are always needed together by the same kind of client - Readable with read() and close() makes sense as one interface if every reader always needs both; splitting those two apart would be fragmentation without a real client ever needing just one.
A fast interview test for ISP: pick each concrete implementer of an interface and ask "does this class have a real, working body for every method here?" Any method it fakes or throws from is a segregation candidate.
A Printer interface has print(), scan(), and fax(). A SimpleInkjetPrinter can only print. How do you handle this?
"Have SimpleInkjetPrinter implement Printer and just throw NotSupportedException from scan() and fax()."
"That's exactly the ISP violation - SimpleInkjetPrinter would be forced to depend on methods it can't honor. I'd split Printer into separate Printable, Scannable, and Faxable interfaces, and have SimpleInkjetPrinter implement only Printable. A MultiFunctionPrinter can implement all three without any class ever faking a capability it doesn't have."
What does the Interface Segregation Principle state?