Commissioned for this museum · after Gamma, Helm, Johnson & Vlissides, 1994
Adapter
TypeScript·1994·32 lines·774 bytes
/**
* Adapter — a thin object presenting one interface in the vocabulary of
* another.
*/
// What the rest of the museum's code has agreed to talk to.
interface Clock {
now(): string;
}
// What it actually has: something older, with its own manners, which cannot
// be changed because it is not ours.
class LegacyTimer {
getEpochMillis(): number {
return 1_700_000_000_000;
}
}
class LegacyTimerAdapter implements Clock {
constructor(private readonly timer: LegacyTimer) {}
now(): string {
return new Date(this.timer.getEpochMillis()).toISOString();
}
}
// This function has never heard of LegacyTimer, and never will.
function report(clock: Clock): void {
console.log("the time is " + clock.now());
}
report(new LegacyTimerAdapter(new LegacyTimer()));Curator’s note
This is the least glamorous pattern in the book and almost certainly the one you have written most often. It has no clever mechanism. It is a small object whose whole job is to know two vocabularies and translate between them, and its value is negative space: report never learns that LegacyTimer exists, so on the day the legacy timer is finally deleted, report does not change.
The pattern exists because you do not control both sides. If you did, the obvious move is to change the old interface, and you should — an adapter added to code you own is usually an admission that a rename felt too frightening. The honest cases are the ones where the other side belongs to somebody else: a library, a vendor API, a service written by a team that is not taking requests.
Notice what the adapter quietly decides. getEpochMillis returns a number and now returns an ISO string, so the choice of format lives here, in the seam, rather than at either end. That is the right place for it and it is easy to miss, which is why adapters have a way of accumulating: a conversion becomes a default, a default becomes a fallback, and eventually the thin translation layer is where the interesting decisions are being made without anyone having decided to put them there.
The rule that keeps one thin is that an adapter should be boring enough that you would not bother writing a test for it. On the day you want to, it has stopped being an adapter.