Ducks Pretending to Be Turkeys (Adapter & Facade)
Two wrappers that look identical in a diagram and exist for opposite reasons: one makes the wrong thing fit, the other hides the whole wall of switches.
The idea
You've lived this: your code expects one shape of thing and the thing you were handed is another shape. Your app speaks Duck with quack and fly; the vendor library ships Turkey with gobble and a fly that only goes five feet. Nobody is wrong here, they were just built by people who never met. Rewriting your working client is risky, forking the vendor means owning their bugs, and scattering type checks everywhere is how you got into trouble in module one.
The fourth option is a travel plug. An adapter implements the interface your code expects and holds the incompatible object inside, translating every call: quack becomes gobble, one duck fly becomes five turkey flies. What it must never do is add behavior. The moment it starts counting or enhancing, it has quietly become a decorator. Decorator keeps the interface and adds responsibility; adapter changes the interface and adds nothing.
The second wrapper solves a completely different problem. A home theater is six classes, and watching a movie means calling them in a precise thirteen step ritual: popper on, lights down, screen down, projector on, wide screen, amp on, volume, play. Nothing is incompatible, there is just far too much surface for a client that only wants movie night. A facade offers the few high level calls people actually want and performs the ritual internally, while leaving the subsystem classes public so a power user can still walk up to the amplifier directly.
Underneath the facade sits the chapter's real prize: the Principle of Least Knowledge. A chain like theater.getAmp().getTuner().setFrequency(101.5) is a train wreck, coupling you to every car in the train. A method should only call methods on itself, its parameters, things it created, and its own components. Facades largely exist to give clients one friend so they never have to befriend the whole subsystem.
The bench — 4 exercises
The Turkey Problem
Make a vendor class your client was never written for drop into that client untouched, by writing an adapter and nothing else.
- Write a small duck simulator client with a Duck interface (quack, fly) and a drill function that quacks once then flies once, then freeze it: do not edit it again.
- Create a vendor/ folder containing WildTurkey with gobble() and a fly() that covers five units, and record a sha256 of each file so you can prove you never edited it.
- Write TurkeyAdapter implementing Duck and wrapping a Turkey: quack delegates to gobble, one duck fly issues five turkey flys.
- Have the vendor class append every call it receives to an in-memory recorder list, then run the frozen drill against new TurkeyAdapter(new WildTurkey()) and print the recorder.
hint
Everything that makes up the difference between the two interfaces belongs inside the adapter. The one-to-five fly loop is adapter code, never client code and never a vendor patch.
hint
If you feel tempted to touch either frozen side, you have found exactly the temptation the pattern exists to kill.
DONE WHEN
· The recorder shows exactly [gobble, fly, fly, fly, fly, fly].
· Re-running your sha256 check on the vendor folder shows unchanged checksums.
· The client file has zero diff against the version you froze.
· TurkeyAdapter declares the Duck interface as its type.
The Generation Gap
Rebuild the book's Enumeration-to-Iterator bridge in your own language, in both directions, and decide honestly what happens to the operation that cannot be mapped.
- Write a deliberately old-style source: a class exposing records only via hasMore()/next() or a forEachRecord(callback) method.
- Write an adapter that exposes it through your language's native iteration protocol (Iterator, __iter__, Symbol.iterator, or a range-able func) and loop over it with ordinary modern client code.
- Now reverse it: write an adapter that lets a legacy consumer walk a modern collection through the old interface.
- Handle the operation the old interface promises but the new world cannot honor (remove or reset) by raising a clear unsupported-operation error rather than a silent no-op.
hint
Adapt in the direction of the caller: start from what the client loop needs, then pull from the legacy side to satisfy it.
hint
If your adapter buffers the whole legacy result set up front, you built a converter, not an adapter. Translate lazily, call by call.
DONE WHEN
· The modern loop over the legacy source yields the same order and contents as iterating the source directly.
· The legacy consumer produces identical output when fed a modern collection through the reverse adapter.
· Calling the unmappable operation raises a named unsupported-operation error, and you can point to the line that raises it.
Movie Night
Collapse a thirteen-line subsystem ritual into one watchMovie() call, and observe that you added no capability at all.
- Build six subsystem classes (Amplifier, Tuner, Projector, Screen, TheaterLights, PopcornPopper) that each append their method name to one shared call recorder.
- Write the painful client first: a movie_night function that is thirteen literal subsystem calls, plus an eight-call end_movie duplicated in two places.
- Create HomeTheaterFacade that receives the six components in its constructor (it composes, it never constructs its world) and move the two rituals verbatim into watchMovie(title) and endMovie().
- Rewrite the client so it calls only the facade, and diff the recorder output before and after.
hint
Don't reverse-engineer the call order; the facade body should be the old thirteen-line block, moved unchanged.
hint
Resist adding anything new inside the facade. It choreographs existing capability, it does not invent any.
DONE WHEN
· The recorder sequence after the facade rewrite is byte-identical to the sequence from the original thirteen-line client.
· The client file contains zero direct subsystem calls.
· The six subsystem classes are unchanged, and you can still call Amplifier directly from a scratch script.
Train Wreck Removal
Turn the Principle of Least Knowledge from a slogan into a script that fails your build, then make it green.
- Write a small status-dashboard client full of chains like theater.getAmp().getTuner().getFrequency() and theater.getProjector().getLamp().getHoursRemaining().
- Write a crude checker (regex or AST) that flags any call made on the result of a getter chain deeper than one hop, printing file and line.
- Fix each violation by pushing a one-line delegating method onto whoever already owns the object you were reaching into, until the checker reports zero.
- Count how many classes the dashboard can name before and after, and re-run the exercise 03 recorder check to prove behavior did not change.
hint
For each wreck, ask who already holds the object I am reaching into. That owner gets the new method, and you call it.
hint
If a facade method itself chains two hops, push again. Demeter applies inside the facade too.
hint
Expect more methods, not fewer. Demeter trades method count for coupling on purpose.
DONE WHEN
· The checker reports zero violations across the dashboard and the duck client.
· The dashboard's reported values are identical to before the refactor.
· The dashboard now names one collaborator instead of five.
· Running the same drill through TurkeyAdapter yields the same vendor-call totals as manual translation, proving the adapter added no behavior.
Go deeper (after the bench)
Read Head First Design Patterns chapter 7 now, after exercise 01: the duck and turkey pages become a victory lap once you've fought the frozen vendor folder, and the Least Knowledge section lands harder with the linter already written. For a free second angle, Refactoring.Guru's Adapter and Facade write-ups give intent-first explanations, structure diagrams, and multi-language examples, and its Relations with Other Patterns section previews the proxy comparison coming in module 10.