Symptom

A class handles multiple variants of an external concern — different library versions, vendor APIs, transport protocols — branching internally with `if (version === 1)` or `if (vendor === 'X')`. Adding or upgrading a variant touches the host class even though the variant logic is fundamentally external.

Goal

Each variant lives in its own Adapter class wrapping the external API. The host class holds a reference to one adapter and delegates; switching variants is a constructor argument, not an internal branch.

Before the refactoring

// One class branches on library version internally.
class ChartGenerator {
draw(data, libraryVersion) {
if (libraryVersion === 1) {
const ctx = OldChartLib.createContext();
ctx.setData(data.map(d => ({ x: d.label, y: d.value })));
ctx.setTitle(this.title);
ctx.render();
return ctx;
}
if (libraryVersion === 2) {
const renderer = new NewChartLib.Renderer({ data, title: this.title });
renderer.render();
return renderer;
}
throw new Error(`unsupported version ${libraryVersion}`);
}
}

After the refactoring

// One adapter per version; ChartGenerator no longer knows the library.
class ChartGenerator {
constructor(chartAdapter) {
this.chartAdapter = chartAdapter;
}
draw(data) {
return this.chartAdapter.draw(data, this.title);
}
}
class V1ChartAdapter {
draw(data, title) {
const ctx = OldChartLib.createContext();
ctx.setData(data.map(d => ({ x: d.label, y: d.value })));
ctx.setTitle(title);
ctx.render();
return ctx;
}
}
class V2ChartAdapter {
draw(data, title) {
const renderer = new NewChartLib.Renderer({ data, title });
renderer.render();
return renderer;
}
}
Example source: Adapted from Joshua Kerievsky's third-party-library-version example in Refactoring to Patterns (Addison-Wesley, 2004), chapter 6. The Java original wrapped two versions of a charting library; this JavaScript translation keeps the same shape — a host class delegating to a per-version adapter.
Pressure

Mixing variant-specific code with host logic produces Divergent Change — the class changes for unrelated reasons (host behaviour evolves; library upgrades). Test setup must mock the external library, which couples host tests to the library's surface area.

Tradeoff

Each adapter is one more file; the host class gives up direct knowledge of the external API and must define a clean wrapping interface. Mapping a host concept to an adapter API forces design choices that don't pay until the second adapter exists.

Relief

Adapters can be swapped per environment, tested in isolation against the real external API, and replaced when the external library is replaced. The host class stops paying the variant-knowledge tax on every read.

Trap

Extracting an adapter for a single, stable external system buys nothing — the wrapper is just one more layer the reader hops through. The pattern pays when there are multiple variants or when the external API is unstable enough that wrapping it earns testability.