Adapter & Facade
Strukturmuster zum Bändigen von Alt- und Fremdcode: Adapter passt eine inkompatible Schnittstelle an, Facade setzt eine einfache Front vor das unordentliche LedgerManager-Subsystem.
Deutsche Übersetzung in Arbeit
Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.
Auf dieser Seite
Two structural patterns are your best friends when dealing with code you can't or shouldn't change - legacy subsystems and third-party libraries. Adapter makes an incompatible interface fit the one your code expects. Facade puts a simple, purpose-built front on a complicated subsystem. Both let you wrap rather than rewrite - exactly the evolutionary approach this path champions.
Adapter: making a square peg fit
You depend on an interface; a class you can't modify offers a different one. An Adapter sits between them,
translating calls. ledger-legacy wants everything to implement its domain ReportSender, but a new
third-party SMS gateway exposes dispatchTextMessage(number, body):
interface ReportSender { void send(Report r); } // what our code expects
class TwilioGateway { // third-party, can't change
void dispatchTextMessage(String number, String body) { ... }
}
class TwilioReportSenderAdapter implements ReportSender { // the ADAPTER
private final TwilioGateway twilio;
private final String recipient;
public void send(Report r) { // translate our call...
twilio.dispatchTextMessage(recipient, r.asShortText()); // ...into theirs
}
}Now the third-party class plugs into our world through the adapter, and our domain code stays blissfully unaware of Twilio. Adapter is the standard way to keep vendor libraries at the edge (a preview of hexagonal architecture's "adapters"): the messy foreign interface is quarantined in one wrapper, and swapping vendors means writing one new adapter.
Facade: a simple door to a complex building
A Facade provides a simplified interface to a complicated subsystem. The LedgerManager is a tangle of
parsing, fee calculation, persistence, and formatting; a client that just wants "import this file and give me a
summary" shouldn't have to orchestrate all of it:
class LedgerFacade { // one simple entry point
private final CsvTransactionParser parser;
private final FeeCalculator fees;
private final TransactionRepository repo;
private final ReportFormatter formatter;
public ImportSummary importFile(Path csv) { // hides the whole dance
var txns = parser.parseAll(csv);
txns.forEach(t -> { t.applyFee(fees.calculate(t)); repo.save(t); });
return formatter.summarize(txns);
}
}The subsystem classes still exist and can be used directly by code that needs fine control; the facade just offers a convenient front for the common case. It reduces coupling - callers depend on the small facade, not on five collaborators and their wiring.
Adapter vs. Facade
They both wrap, so the distinction matters:
- Adapter changes an interface to match an existing expected one - the shapes are dictated by both sides, and the goal is compatibility. Usually wraps one class.
- Facade invents a new, simpler interface over a subsystem - the shape is whatever's convenient, and the goal is simplification. Usually fronts many classes.
Adapter answers "this doesn't fit"; Facade answers "this is too complicated."
A facade isn't a license to leave the mess
Wrapping LedgerManager in a facade is a great first step to tame a legacy tangle - callers get a clean door today. But the mess still lurks behind it. Use the facade as a stable boundary while you refactor the internals (as this path does), not as a permanent lid on rot. The danger is a facade that quietly becomes another god class as everyone piles convenience methods onto it.
An adapter is the plug adapter in your suitcase: your laptop's plug (your expected interface) doesn't fit the foreign socket (the third-party interface), so the adapter translates between the two shapes - it doesn't change the laptop or the wall, just bridges them. A facade is the hotel concierge: behind them is a bewildering subsystem - housekeeping, the kitchen, ticketing, taxis - but you just say 'dinner reservation and a cab at 7,' and they orchestrate it all. The concierge invented a simple interface over a complex operation; the plug adapter made two fixed interfaces compatible.
Two integration tasks in ledger-legacy: (1) a new PDF library exposes Document.render(Stream, Options) but all
your export code calls a domain ReportSender.send(Report); (2) generating a monthly close currently requires a
client to call, in order, the parser, validator, fee engine, ledger writer, and notifier - five objects with
fiddly setup - and you want to expose a one-call runMonthlyClose(). Which pattern for each?
What distinguishes an Adapter from a Facade?
Key takeaways
- Adapter and Facade are structural patterns that let you wrap code you can't or shouldn't change, rather than rewrite it.
- Adapter translates an incompatible interface into the one your code expects - the standard way to quarantine third-party libraries at the edge.
- Facade provides a simplified interface over a complex subsystem, reducing coupling by giving callers one small door instead of many collaborators.
- Adapter is about compatibility (usually one class); Facade is about simplification (usually many). 'Doesn't fit' vs. 'too complicated.'
- A facade is a great stable boundary while you refactor the mess behind it - not a permanent lid, and watch it doesn't grow into a new god class.