2.2.7 · D2Design Principles

Visual walkthrough — SOLID — Interface Segregation Principle

1,866 words8 min readBack to topic

We start with nothing but two words. Let us earn them.


Step 1 — What an "interface" even is (a menu of promises)

WHAT. An interface is a list of things a piece of code promises it can do. Nothing more. It has no bodies, no logic — just names of abilities: work(), eat(), print(). Think of it as a menu: a printed list of dishes the kitchen swears it can cook.

WHY start here. Every later picture is about who is forced to sign this menu. If you don't picture the menu first, the word "depend" later means nothing.

PICTURE. On the left, a menu card listing three promises. On the right, a class — the "kitchen" — that must be able to deliver every line it signs.

Figure — SOLID — Interface Segregation Principle

Step 2 — The fat interface: one menu, forced on everyone

WHAT. A fat interface bundles unrelated promises into a single menu. Our running example is Worker, holding two unrelated abilities:

Here and are just the two names on the menu; the brace groups them into one interface called Worker.

WHY it looks fine. Both are things "a worker does", so lumping them feels natural — this is the trap.

PICTURE. One menu Worker with an arrow forcing it onto two very different signers: a Human (who genuinely eats) and a Robot (who does not). The robot is drawn in the accent colour — it is the problem child of every figure ahead.

Figure — SOLID — Interface Segregation Principle

Step 3 — Where it breaks: the forced lie

WHAT. Because the Robot signed the whole menu, it must provide a body for eat(). Having no notion of eating, it writes:

public void eat() { throw new UnsupportedOperationException(); }

WHY this is a lie. The type Worker claims the robot can eat. The code inside admits it cannot. The compiler sees a satisfied menu and stays silent; the truth only explodes at runtime when someone actually calls robot.eat().

PICTURE. The robot's eat() slot filled with a red "bomb" — it compiles green but detonates when called. Contrast the human's honest, working eat().

Figure — SOLID — Interface Segregation Principle

Step 4 — The core rule, stated on the picture

WHAT. We can now read the principle straight off Step 3:

WHY the word "forced" is the whole point. The robot never asked for eat(); the menu forced it. Every ISP fix is just: remove the force.

PICTURE. The single fat arrow of Step 2, now labelled forced, with a red slash through it — the thing we are about to delete.

Figure — SOLID — Interface Segregation Principle

Step 5 — The fix: split by role, not by method

WHAT. Apply the parent's recipe: group methods by which clients use them together (their role), then make one interface per role.

Read this as: the two-promise menu becomes two one-role menus. Workable holds the working role; Feedable holds the eating role.

WHY split by role and not by every method. We split because work() and eat() serve different clients (a job scheduler vs. a canteen). We do not atomise blindly — if two methods were always used together by the same client, they'd stay in one interface. The dividing line is cohesion (see Cohesion and Coupling), not minimum size.

PICTURE. The fat Worker menu torn along the role seam into two clean cards.

Figure — SOLID — Interface Segregation Principle

Step 6 — Recompose: a class wears the roles it needs

WHAT. Now each class signs only the menus that describe it, and may sign several:

class HumanWorker implements Workable, Feedable { ... }  // both roles
class RobotWorker implements Workable            { ... }  // one honest role

WHY this is honest. The robot's contract now says exactly what it can do — no eat(), no bomb, no lie. A canteen scheduler is written against Feedable; it cannot even name a RobotWorker, so the runtime bug of Step 3 becomes impossible to compile.

PICTURE. Human signs both cards; robot signs only Workable. The red robot is finally telling the truth — drawn without any bomb.

Figure — SOLID — Interface Segregation Principle

Step 7 — Measuring the win (the numbers, drawn)

WHAT. Let a fat interface have methods and implementers, where implementer genuinely uses of them. The forced (dead) dependencies are the gaps summed over everyone:

  • — how many promises each implementer is forced to sign.
  • — how many that implementer actually needs.
  • — the dead weight for implementer (the red gap).

After perfect segregation each class pulls in exactly , so every gap is zero: .

WHY this formula. It turns "feels bloated" into a countable number, so you can see the improvement rather than argue about it.

Numbers (the printer example). (print/scan/fax), implementers use :

Four forced dependencies vanish under ISP.

PICTURE. A bar chart: for each of the three printers, the tall bar is ; the red slice on top is the forced gap . The red slices sum to 4.

Figure — SOLID — Interface Segregation Principle

Step 8 — The degenerate cases (so nothing surprises you)

WHAT & WHY. Check the edges of the formula so no scenario is left unshown:

Case Condition Meaning
Everyone uses everything for all Interface is already cohesive — do not split; that would be over-splitting (Step 5).
Someone uses nothing per such class Maximum lie — the whole menu is dead weight for that class.
One implementer, any Even a single class can suffer forced dependencies.
After segregation roles matched to need The target state.

PICTURE. Two extreme bars side by side: left, a cohesive interface (no red, leave it alone); right, a class using nothing (all red, the pure-lie case).

Figure — SOLID — Interface Segregation Principle

The one-picture summary

Everything on one card: the fat Worker menu (top) forces eat() onto a robot (the bomb), so we tear it along the role seam into Workable + Feedable (bottom), and the robot signs only what is true.

Figure — SOLID — Interface Segregation Principle
Recall Feynman retelling — the whole walkthrough in plain words

An interface is a menu of things code promises it can do. A fat menu (Worker) forces every signer to promise every dish — so a Robot, who cannot eat, still has to sign eat(). It fakes it with a stub that throws — a lie that compiles but explodes when called. The fix: tear the menu along its role seams — a work menu and an eat menu — and let each class sign only the menus that are honestly true of it. The Human signs both; the Robot signs only Workable, and now the eating-bug can't even be written. To prove the win we count forced dishes: each signer promises but needs only , so the wasted promises are — which becomes zero after we split. And the catch: only tear where waste actually shows up (where the bars turn red). If everyone already uses everything, leave the menu alone.


Connections

Concept Map

causes

leads to

result

Interface is a menu of promises

Fat menu forces every signer

Robot fakes eat with a bomb

Rule remove the force

Split by role not method

Class wears roles it needs

Count forced dishes m minus u

Segregation gives zero