Card 04/ 08
Interface Segregation Example: The Xerox Printer Job
One Job class used by every part of the printer, so every change rebuilt everything.
The interface segregation principle came from a real system, and its story shows the client side of the harm. This example retells it, then shows the fix in a small Python listing, where a printing function and a stapling function each see only their own part of a job.
In the early 1990s, Xerox built software for a printer that could also staple and fax. Robert C. Martin, consulting on it, found one Job class used by every task: printing, stapling, faxing. Each task needed only a few of its methods, but each depended on all of them. A change for stapling forced the printing code to be rebuilt too, and a build took about an hour. Developers stopped making small changes because each one cost an hour.
Martin's fix was to put an interface in front of Job for each task: a print job interface, a staple job interface. Job still implemented them all, but each task's code depended only on its own interface. A change to stapling no longer touched printing.
from typing import Protocol
class PrintJob(Protocol):
def pages(self) -> int: ...
class StapleJob(Protocol):
def staple_positions(self) -> list[str]: ...
class Job:
def __init__(self, name, pages, staples):
self.name = name
self._pages = pages
self._staples = staples
def pages(self) -> int:
return self._pages
def staple_positions(self) -> list[str]:
return self._staples
def print_pages(job: PrintJob) -> None:
print(f"printing {job.pages()} pages")
def staple(job: StapleJob) -> None:
print(f"stapling at {', '.join(job.staple_positions())}")
report = Job("quarterly report", 12, ["top-left"])
print_pages(report)
staple(report)printing 12 pages
stapling at top-leftThe listing keeps one Job class, and that is the point of the example. Interface segregation does not require splitting the implementation. It splits the view clients have of it. print_pages depends on PrintJob, which knows only about pages; staple depends on StapleJob. The same report object is passed to both.
The cost Martin removed was rebuild time, which is a compiled-language concern. In Python the cost shows up differently, as code that must be re-read and retested when a method it never calls changes. The principle is the same in both: a client should change only when something it uses changes.
← → move through sections and cards