Adapter Pattern: The Travel Plug Trick That Makes Old Code Fit New Code
Learn the Adapter pattern with a simple 3-pin plug and 2-pin socket story. Make old code work with new code without changing either side. Easy examples.
๐ The 3-pin plug that would not fit
Let me tell you a small story from Aarav's summer holidays. Keep this story in your head, because every single section of this post will come back to it.
Aarav is fourteen. Every May, he goes to his Dadi's old house in a village near Nashik. The house is lovely โ cool stone floors, a mango tree in the courtyard, and a noisy ceiling fan that sounds like a helicopter. But the house was built many years ago, and every wall socket there is an old 2-pin socket.
On the first evening, Aarav opened his bag and took out his laptop charger. The charger has a big, modern 3-pin plug. He bent down near the wall and tried to push it in. It did not fit. He turned it this way and that way. He pressed harder. Still no luck. Three pins simply cannot enter two holes. The laptop battery icon was at 9 percent and falling.
Dadi watched all this drama from her chair and smiled. "Beta, the wall is older than your father. It will not change for your laptop."
Now think about Aarav's choices, the same way a programmer would.
- Change the new side? Should he cut the wire of his costly charger and fix a 2-pin plug on it? No! That would spoil the charger, and it may not work properly back in his own house in Mumbai.
- Change the old side? Should he break Dadi's wall and fit a new 3-pin socket? Definitely not! Dadi would not be happy, the mason would take two days, and all this for one charger?
- Add a middle piece? Aarav walked to Sharma Electricals near the bus stand and bought a small plug adapter for forty rupees. One side of the adapter has three holes that accept his charger. The other side has two pins that fit Dadi's socket. He plugged the charger into the adapter, the adapter into the wall, and the laptop started charging.
Notice the most important part of this story. Nothing was changed. The charger stayed exactly as it was. The wall socket stayed exactly as it was. A small, cheap middle piece did all the adjusting.
There is a second version of this same idea in Aarav's family. His cousin Sana studies in Chennai and speaks Tamil with her college friends. Dadi speaks only Marathi. When Sana's Tamil-speaking friend Kavya visited Nashik, Dadi and Kavya could not talk at all โ until Sana sat between them. Sana listened in Tamil and spoke in Marathi, then listened in Marathi and spoke in Tamil. She did not ask Dadi to learn Tamil. She did not ask Kavya to learn Marathi. She translated in the middle, and the conversation flowed for two full hours, mostly about food.
The forty-rupee plug and cousin Sana are doing the same job. In software, that job has a name: the Adapter pattern.
Before we touch any code, here is Aarav's whole evening as a journey. See how the mood score jumps the moment the adapter enters.
๐งฉ What is the Adapter pattern?
Here is a simple definition you can remember for life.
The Adapter pattern is a structural design pattern that lets two classes with incompatible interfaces work together. It does this by putting a translator class in the middle. The translator presents the interface your code expects, and inside, it calls the methods the other class actually has.
In design-pattern books, the people in Aarav's story have special names. Learn these four words well โ they appear in every textbook, every interview, and every team discussion.
| Pattern role | Meaning | In Aarav's story |
|---|---|---|
| Client | Your existing code, the one that wants to call something | Aarav's laptop and its charger |
| Target | The interface the client expects | The 3-pin shape |
| Adaptee | The useful but incompatible thing that already exists | Dadi's 2-pin wall socket |
| Adapter | The middle piece that implements the Target and talks to the Adaptee | The forty-rupee plug from Sharma Electricals |
The Adapter pattern is also called the Wrapper pattern, because the adapter wraps the old object inside itself like a cover. The wall socket is still there โ it is just hidden behind the adapter, the way Dadi's Marathi was hidden behind Sana's translation.
Golden rule of the Adapter pattern: never change the two sides โ only add a middle piece. The client keeps calling the interface it always knew. The old class keeps its old methods. All the conversion work lives inside one small adapter class. If tomorrow the old class changes, you only fix the adapter, not your whole program.
Here is the whole idea in one picture, so you can see the full map before we walk through it.
๐ฆ๏ธ The problem it solves
Let us move from plugs to code, but keep the same shape of problem. Imagine Aarav's school is building a small weather display app for the notice-board screen. Aarav wrote the display code, and it reads temperatures as a simple list of numbers:
// The interface Aarav's display code already uses everywhere.
interface TemperatureSource {
readCelsiusValues(): number[];
}
function showWeather(source: TemperatureSource) {
const values = source.readCelsiusValues();
console.log("Today's readings:", values.join(", "));
}Everything works nicely. Then the science teacher buys a ready-made sensor library from outside. It is fast and well tested, but it gives data in a completely different style โ an XML string from a method with a different name:
// Third-party class. Aarav CANNOT edit this file.
class XmlWeatherSensor {
pullReadingsXml(): string {
return "<readings><r>31</r><r>33</r><r>29</r></readings>";
}
}Aarav's display wants readCelsiusValues() returning numbers. The sensor only offers pullReadingsXml() returning XML text. They simply do not fit โ three pins, two holes, all over again. The laptop charger has become showWeather(), and Dadi's wall has become XmlWeatherSensor.
Without the Adapter pattern, students usually fall into one of these traps:
// BAD: conversion code copied at every place that needs the sensor.
const sensor = new XmlWeatherSensor();
const xml = sensor.pullReadingsXml();
const numbers = xml
.split("<r>")
.slice(1)
.map((part) => parseInt(part));
showWeather({ readCelsiusValues: () => numbers });
// ...and the same five lines appear again in ten other files!This is painful for three reasons. First, the same parsing logic gets copied everywhere, and when the XML format changes, you must hunt down every copy. Second, you might feel tempted to edit the third-party library, but you do not own it, and your edits will vanish on the next update โ exactly like repainting a rented wall. Third, you might think of rewriting the display code to understand XML, but then every other data source must also become XML. All three options are the coding versions of cutting the charger or breaking Dadi's wall.
Here is a number that surprises most students. In real integration projects, the boring conversion work โ renaming fields, changing units, reshaping data โ quietly eats more time than the actual logic:
More than half the effort is pure translation. The Adapter pattern does not remove that work โ translation must happen somewhere โ but it collects all of it into one small, named place instead of letting it leak into every file.
And the leak grows fast. Watch what happens as more and more places in the app need the sensor:
The bars are the copy-paste approach: one copy of the parsing logic per call site. The flat line is the adapter approach: always exactly one, no matter how big the app grows. That flat line is the whole sales pitch of this pattern.
๐ ๏ธ How it works, step by step
Building an adapter is like following a simple recipe. Here are the steps, one by one.
- Confirm the mismatch is real. You have a client that expects one interface, and a useful class that offers a different interface. You cannot (or should not) change either of them.
- Pick the Target interface. This is what your client already calls. If it exists, reuse it. Do not invent a new one without reason.
- Create the Adapter class and declare that it implements the Target interface.
- Give the adapter a field that holds the adaptee object. Pass the adaptee in through the constructor. This is called an object adapter, and it is the form you should normally use.
- Implement each Target method inside the adapter. Each method calls the adaptee's real methods and converts the data โ change formats, rename ideas, reshape arguments, translate errors.
- Hand the adapter to the client. The client only sees the Target interface. It never learns that an old class is hiding behind it.
The structure looks like this:
Read the diagram from left to right. The client only knows the interface. The adapter implements that interface and quietly holds the incompatible sensor. The arrow from adapter to sensor is the bridge of translation โ every expected call goes in one side and comes out in the old style on the other side.
Now watch a single call travel through the layers, like current flowing from the wall, through the forty-rupee adapter, into the charger:
Three small things to notice in this sequence. The client makes one normal call and has no idea anything special happened. The adaptee answers in its own old style and is never disturbed. And the only clever step โ the parsing โ happens inside the adapter, in exactly one place.
You can also describe the life of the adaptee as a tiny state machine. Before the adapter exists, the old class is useful but unreachable. After wrapping, every request flows:
College corner: there are actually two ways to build an adapter, and exam papers love asking the difference. The object adapter (what we built above) holds the adaptee as a field โ this is composition, the has-a relationship. The class adapter inherits from the adaptee and implements the target interface at the same time โ this is inheritance, the is-a relationship. A class adapter needs multiple inheritance of classes, which C++ allows but Java, C#, and TypeScript do not (they only allow implementing multiple interfaces). The object adapter wins in practice for three more reasons: it can adapt the adaptee and all its subclasses at once, since any of them can be passed into the constructor; it can wrap an object that is created elsewhere at runtime; and it keeps you loosely coupled, because you depend on the adaptee's public surface, not on its internals. The class adapter's only real advantages are saving one object allocation and being able to override a protected method of the adaptee. Here is the comparison in table form:
| Question | Object adapter | Class adapter |
|---|---|---|
| Connection to adaptee | Holds it as a field (composition) | Inherits from it (inheritance) |
| Works in Java, C#, TypeScript? | โ Yes, everywhere | โ Needs multiple class inheritance (C++) |
| Can adapt subclasses of the adaptee? | โ Yes โ pass any subclass in | โ No โ locked to one parent |
| Can wrap an object created at runtime? | โ Yes | โ No |
| Can override adaptee's protected behaviour? | โ No | โ Yes |
| Recommended? | โ Default choice | Only in special C++ cases |
๐ป Real-life code example
Now let us write the full story in TypeScript โ and yes, we will carry Aarav's plug adventure straight into the code. The laptop is the client. The 3-pin socket shape is the target interface. Dadi's old wall socket is the adaptee. And our hero, the forty-rupee adapter, is the adapter class.
// ---------- TARGET: the interface the laptop expects ----------
// Modern laptops in our story charge only from 3-pin sockets.
interface ThreePinSocket {
// Returns power with three lines: live, neutral, and earth.
givePower(): { live: number; neutral: number; earth: number };
}
// ---------- CLIENT: the laptop ----------
// The laptop is written against ThreePinSocket only.
// It knows NOTHING about old 2-pin sockets.
class Laptop {
charge(socket: ThreePinSocket): void {
const power = socket.givePower();
if (power.earth === 0) {
console.log("Charging safely with earthing. Battery filling up!");
}
}
}
// ---------- ADAPTEE: Dadi's old wall socket ----------
// This class already exists. We must NOT change it.
// Note: different method name, different return shape. No earth line!
class OldTwoPinSocket {
supplyCurrent(): { live: number; neutral: number } {
return { live: 230, neutral: 0 };
}
}
// ---------- ADAPTER: the forty-rupee hero ----------
// It implements the NEW interface and wraps the OLD socket.
class TwoToThreePinAdapter implements ThreePinSocket {
// The adapter HOLDS the old socket. This is composition.
constructor(private oldSocket: OldTwoPinSocket) {}
givePower(): { live: number; neutral: number; earth: number } {
// 1. Call the old method with the old name.
const current = this.oldSocket.supplyCurrent();
// 2. Translate the old shape into the new shape.
// The adapter adds a safe earth connection of its own.
return { live: current.live, neutral: current.neutral, earth: 0 };
}
}
// ---------- WIRING IT TOGETHER ----------
const dadisSocket = new OldTwoPinSocket(); // old thing, untouched
const adapter = new TwoToThreePinAdapter(dadisSocket); // middle piece
const laptop = new Laptop(); // new thing, untouched
laptop.charge(adapter); // the laptop has no idea an old socket is behind!
// Output:
// Charging safely with earthing. Battery filling up!Look closely at what happened. The Laptop class never imports OldTwoPinSocket. The OldTwoPinSocket class never heard of ThreePinSocket. Only TwoToThreePinAdapter knows both worlds โ and translation lives in exactly one place: inside givePower().
Want to see the weather example finished too? Here is the adapter for the XML sensor, short and sweet:
class SensorAdapter implements TemperatureSource {
constructor(private sensor: XmlWeatherSensor) {}
readCelsiusValues(): number[] {
const xml = this.sensor.pullReadingsXml();
// All XML parsing lives HERE and only here.
return xml.split("<r>").slice(1).map((p) => parseInt(p));
}
}
showWeather(new SensorAdapter(new XmlWeatherSensor()));
// Output:
// Today's readings: 31, 33, 29If the sensor company changes its XML format next year, Aarav opens one file, fixes one method, and the whole notice-board app keeps working. That is the power of keeping translation in a single place.
๐ The same idea in C# and Python
The pattern looks almost the same in every object-oriented language, because the idea โ implement the new, hold the old โ does not depend on syntax. Here is a compact version of the plug story in C#:
// Target: what the client expects.
public interface IThreePinSocket
{
(int Live, int Neutral, int Earth) GivePower();
}
// Adaptee: the old class we cannot change.
public class OldTwoPinSocket
{
public (int Live, int Neutral) SupplyCurrent() => (230, 0);
}
// Adapter: implements the new, wraps the old.
public class TwoToThreePinAdapter : IThreePinSocket
{
private readonly OldTwoPinSocket _oldSocket;
public TwoToThreePinAdapter(OldTwoPinSocket oldSocket)
=> _oldSocket = oldSocket;
public (int Live, int Neutral, int Earth) GivePower()
{
var (live, neutral) = _oldSocket.SupplyCurrent();
return (live, neutral, 0); // translate old shape to new shape
}
}
// Client code:
IThreePinSocket socket = new TwoToThreePinAdapter(new OldTwoPinSocket());
var power = socket.GivePower();
Console.WriteLine($"Charging with live = {power.Live}V");
// Output: Charging with live = 230VAnd here is the translator version โ cousin Sana herself โ in Python. Python has no interface keyword, but duck typing plays the same role: whoever has a speak_marathi() method can talk to Dadi.
# Adaptee: Kavya only speaks Tamil. We cannot change her!
class TamilSpeaker:
def speak_tamil(self) -> str:
return "Vanakkam! Sapad romba nalla irukku."
# Adapter: Sana wraps Kavya and presents Marathi to Dadi.
class SanaTheTranslator:
def __init__(self, friend: TamilSpeaker):
self._friend = friend # composition: Sana HOLDS her friend
def speak_marathi(self) -> str:
tamil = self._friend.speak_tamil()
# Translation logic lives here, and only here.
return "Namaskar! Jevan khup chhan aahe. (translated)"
# Client: Dadi understands anyone who speaks Marathi.
def dadi_listens(speaker) -> None:
print("Dadi hears:", speaker.speak_marathi())
dadi_listens(SanaTheTranslator(TamilSpeaker()))
# Output: Dadi hears: Namaskar! Jevan khup chhan aahe. (translated)Same recipe in all three languages: an interface the client trusts, an old object held as a private field, and one method doing the translation.
College corner: notice what the adapter costs at runtime โ one extra object and one extra method call per request. On modern hardware this is nanoseconds, and JIT compilers often inline the delegation completely. So never reject an adapter for "performance reasons" in normal application code. The real cost of an adapter is conceptual: one more class to name, test, and document. That cost is worth paying when translation logic would otherwise be duplicated, and not worth paying for a one-line conversion used once.
๐ Where you see it in real software
The Adapter pattern is not just a classroom idea. Real, famous libraries use it every single day.
- Java's
InputStreamReader. In Java, anInputStreamgives you raw bytes, but many APIs want aReaderthat gives characters.java.io.InputStreamReaderis a textbook adapter: it wraps anInputStreamand presents theReaderinterface, converting bytes to characters inside. Its siblingOutputStreamWriteradapts in the other direction. - Java's
Arrays.asList(). It takes a plain array and presents it through theListinterface โ an array wearing a list costume. - .NET's
StreamWriterover aStream. In C#, aStreamdeals with bytes, while text-based code wants aTextWriter.StreamWriteradapts a byte stream into theTextWriterinterface, so code written for text can write to files, network sockets, or memory without knowing the difference. - JavaScript HTTP wrappers. Many teams write a small adapter around
axiosorfetchso the rest of the app calls a clean, stable interface likeapi.get()andapi.post(). If the team later switches from axios to fetch, only the adapter file changes. The popularaxios-mock-adapterlibrary even has "adapter" in its name โ it slots a fake layer in where the real HTTP layer used to be, for testing. - Payment gateways. Indian apps that accept UPI, cards, and wallets usually write one internal
PaymentGatewayinterface and a separate adapter per provider. Swapping providers becomes a one-file change instead of a month-long rewrite. - Open-source examples to read. The iluwatar/java-design-patterns repository has a friendly Adapter example where a sailor with a rowing boat is adapted to a captain's fishing-boat interface. The design-patterns-for-humans repository explains the same idea with a lion hunter adapting to hunt a wild dog. Both are great weekend reading.
So next time you read a file in Java or C#, remember โ an adapter is probably working quietly under your fingers, exactly like the forty-rupee plug working quietly behind Aarav's laptop.
โ When to use it and when not to
Aarav did not buy an adapter for every problem that summer. When his torch needed new batteries, he just bought batteries. The skill is knowing when the middle-piece trick is the right one. Use this table:
| Situation | Use Adapter? | Why |
|---|---|---|
| A third-party library has the data you need but the wrong interface | โ Yes | This is the classic case โ translate in one place |
| Old legacy code must work with a new system, and you cannot edit the old code | โ Yes | Adapter protects both sides from change |
| Two teams' systems must be joined after a company merger | โ Yes | Each system keeps its own style; adapters join them |
| A flaky, fast-changing library is used in many files | โ Yes | Route it through one adapter so future breakage hits one file |
| You own both classes and can simply rename a method | โ No | Just fix the code directly โ no middle piece needed |
| The conversion is one tiny line used in one single place | โ No | An extra class for this is overkill |
| The two interfaces are completely unrelated in meaning, not just in shape | โ No | An adapter cannot invent missing behaviour, only translate existing behaviour |
| You actually want to add new behaviour, not change the interface | โ No | That job belongs to Decorator, not Adapter |
The same decision drawn as a picture โ find where your situation sits:
The top-right corner โ code you cannot change, used in many places โ is the adapter's home ground. The bottom-left โ your own code, used once โ is where you should simply edit the code and move on.
โ ๏ธ Common mistakes students make
The number one mistake: putting business logic inside the adapter. An adapter should only translate โ change names, convert formats, reshape data. The moment your adapter starts making decisions like "if temperature is above 40, send an alert", it has stopped being an adapter and become a hiding spot for important logic that nobody will ever find. Keep adapters thin, boring, and honest โ Sana translated Dadi's words; she did not start adding her own opinions about the food.
A few more traps to avoid:
- Editing the adaptee "just a little". Students sometimes change one method of the third-party class to make life easier. On the next library update, the change silently disappears and the app breaks. Never touch the adaptee โ Dadi's wall stays as it is.
- Letting the client see the adaptee. If your client code writes
new XmlWeatherSensor()anywhere outside the wiring spot, the wall between the two worlds is broken. The client should only ever see the Target interface. - Making one giant "universal adapter". One adapter class trying to adapt five different libraries becomes a monster. Write one small adapter per adaptee โ one plug per socket type.
- Confusing Adapter with Decorator. If the wrapped object and the wrapper expose the same methods, you have written a Decorator, not an Adapter. An Adapter must change the interface.
- Hiding errors during translation. If the adaptee throws an error in its old style, translate the error too. Swallowing errors inside the adapter makes bugs impossible to trace.
๐ช Compare with cousins
The wrapper family has four famous cousins, and students mix them up all the time. Here is the family album:
| Pattern | Interface seen by caller | Main purpose | One-line memory trick |
|---|---|---|---|
| Adapter | Different from the wrapped object | Make incompatible things fit | "Travel plug" |
| Decorator | Same as the wrapped object | Add extra behaviour (logging, caching) | "Extra cheese on the same pizza" |
| Facade | New, simple interface over many objects | Hide a whole messy subsystem | "Hotel reception desk" |
| Proxy | Same as the wrapped object | Control access (lazy load, security, remote) | "Watchman at the gate" |
A quick litmus test: ask yourself, "Was I forced to match an interface that already existed?" If yes, it is an Adapter. If you invented a nice simple interface yourself to cover many classes, it is a Facade. If the methods stayed the same but gained extra work, it is a Decorator. If the methods stayed the same but access is being controlled, it is a Proxy.
One more cousin worth a sentence: Bridge. Bridge also connects two sides through an interface, but Bridge is planned in advance while designing, whereas Adapter is a rescue applied afterwards when two finished pieces refuse to fit. Aarav did not plan the adapter โ he discovered the mismatch and ran to the shop. We will meet the planned version, Bridge, properly in the next post.
College corner: the Adapter pattern is the everyday tool for honouring two SOLID principles at once. It supports the Open/Closed Principle, because the system gains support for a new, incompatible class without modifying existing client code โ you extend by adding an adapter. And it supports the Dependency Inversion Principle, because the client depends on an abstraction (the Target interface), not on the concrete third-party class. When your professor asks for a practical example of DIP, "an adapter in front of a vendor SDK" is a perfect answer.
๐ฆ Quick revision box
+=====================================================================+
| ADAPTER PATTERN โ REVISION CARD |
+=====================================================================+
| Type : Structural pattern |
| Nickname : Wrapper |
| Story : 3-pin plug + 2-pin socket + 40-rupee adapter |
| |
| Players : Client -> calls the Target interface |
| Target -> interface the client expects |
| Adaptee -> old/3rd-party class, wrong interface |
| Adapter -> implements Target, wraps Adaptee |
| |
| Rule : NEVER change the two sides. Only add a middle piece. |
| Best form : Object adapter (hold adaptee as a field) |
| Keep it : Thin! Only translation, no business logic. |
| |
| vs Decorator: same interface + extra behaviour (not Adapter) |
| vs Facade : new simple front for MANY classes (not Adapter) |
| vs Proxy : same interface + access control (not Adapter) |
| vs Bridge : Bridge is planned; Adapter is a rescue. |
+=====================================================================+๐๏ธ Practice exercise
Time to get your hands dirty! Try these tasks on your own before peeking at any solutions.
-
The music player. Your app plays songs through
interface AudioPlayer { playMp3(file: string): void }. Your friend gives you a classVlcEnginewith a methodstartVlcPlayback(path: string, format: string). Write aVlcAdapterso your app can play.vlcfiles without changing either class. Print a message from each layer so you can see the call travelling through the adapter, exactly like Figure 7. -
The payment gateway. An online shop calls
interface PaymentGateway { pay(amountInRupees: number): string }. The bank's old SDK hasclass LegacyBankApi { makeTransaction(paise: number): { code: number } }โ note it wants paise, not rupees, and returns a code instead of a message. Write an adapter that multiplies rupees by 100, calls the old API, and converts code0to"SUCCESS"and anything else to"FAILED". -
Draw before you code. For exercise 2, draw the sequence diagram on paper first: shop, adapter, bank SDK. Mark where rupees become paise and where the code becomes a message. If both conversions sit inside the adapter and nowhere else, your design is right.
-
Spot the adapters. Open any project you have written (or any open-source repo you like) and find two places where a class wraps another class. For each, decide honestly: is it an Adapter (interface changed), a Decorator (behaviour added), or a Facade (many classes simplified)? Write one sentence of proof for your answer.
If you can finish all four, you have truly understood the Adapter pattern. Aarav's laptop is charging, Dadi's wall is unbroken, and the forty-rupee hero sits between them doing its quiet job. Now go and adapt something!
Frequently asked questions
- What is the Adapter pattern in simple words?
- The Adapter pattern is a middleman class that sits between two pieces of code that cannot talk to each other. It receives a call in the style the new code expects, translates it, and passes it to the old code. Neither side is changed โ only the adapter knows both styles.
- Why is the Adapter pattern also called Wrapper?
- Because the adapter literally wraps the old, incompatible object inside itself. From outside, callers only see the new interface. Inside, the adapter quietly calls the wrapped object's old methods and converts the data.
- What is the difference between an object adapter and a class adapter?
- An object adapter holds the old object as a field and delegates calls to it (composition). A class adapter inherits from the old class and implements the new interface at the same time (inheritance). Object adapters are preferred because they are more flexible and work in every language.
- When should I avoid using the Adapter pattern?
- Avoid it when you can simply change one of the two classes yourself, or when the conversion is a tiny one-line job used in only one place. Adding an adapter class for such small cases only adds extra files and indirection without real benefit.
- Is Adapter the same as Decorator?
- No. Both wrap an object, but a Decorator keeps the same interface and adds extra behaviour like logging or caching. An Adapter changes the interface so an incompatible object can be used. Same methods with extra work means Decorator; different methods means Adapter.
Further reading
Related Lessons
Bridge Pattern: One Remote, Many Devices โ Stop the Subclass Explosion
Understand the Bridge pattern with a TV and remote story. Split one big class into two parts that grow separately, so you avoid too many extra subclasses.
Composite Pattern: Boxes Inside Boxes โ Treat One Thing and Many Things Alike
Learn the Composite pattern with boxes inside a courier parcel. Treat one item and a full group the same way, and add totals easily with simple recursion.
Decorator Pattern: Add Toppings to Your Objects, One Layer at a Time
Learn the Decorator pattern with a simple dosa-toppings story. Wrap objects in layers to add new behaviour at runtime, without making new subclasses.
Facade Pattern: One Travel Agent for Your Whole Messy Trip
Learn the Facade pattern with a travel agent story. Hide many complicated subsystems behind one simple method so client code stays short and clean.