Adapter: The Travel Plug Pattern
This is the easiest design pattern to understand, because you've literally held one in your hand.
The travel plug
You fly from India to the UK. Your phone charger has round Indian pins. The UK wall socket wants flat rectangular pins.
Do you throw away your charger? Rewire the hotel wall? Of course not. You buy a cheap travel adapter. Charger plugs into the adapter, adapter plugs into the wall. Done.
Nothing got modified. The charger is unchanged, the wall is unchanged. The adapter just translates between two shapes that don't fit each other.
That is the entire Adapter pattern.
The code problem
Your app expects one consistent interface everywhere:
public interface INotificationSender
{
void Send(string recipient, string message);
}
Now you pull in a third-party SMS library from NuGet. But its class looks nothing like your interface:
// Third-party code — you CANNOT edit this
public class ThirdPartySmsClient
{
public void PushSms(string phoneNumber, string text, int priority) { /* ... */ }
}
Your app speaks Send(). The library speaks PushSms(). Different name, different parameters. Round pin, flat socket.
You can't edit their code — it's a package. And you don't want to edit the 20 files in your app that already call Send().
The adapter
Write one small class that sits in the middle and translates:
public class SmsAdapter : INotificationSender // wears YOUR interface
{
private readonly ThirdPartySmsClient _client = new(); // holds THEIR class
public void Send(string recipient, string message)
{
// translate your call into their call
_client.PushSms(recipient, message, priority: 1);
}
}
Read what this class is:
-
Outside — it implements
INotificationSender, your shape. -
Inside — it holds a
ThirdPartySmsClient, their shape. - The method body — just translates one call into the other.
Now your app uses it like any other sender, with no idea a translation is happening:
INotificationSender sender = new SmsAdapter();
sender.Send("+9198xxxxxx", "Hello!");
When you'll actually use this
Constantly — any time you integrate something you don't control:
- A NuGet package whose method names don't match your interfaces.
- A legacy class from an old system that new code has to call.
- Two systems that both exist and neither can change — an adapter goes between them.
- Swapping vendors: wrap each vendor's SDK in its own adapter, and switching vendors becomes "write one new adapter" instead of "rewrite the app."
That last point is the real payoff. Your application code depends only on your interface. The messy, ever-changing outside world gets quarantined inside adapters.
How it differs from the creational patterns
- Factory decides which object to create.
- Builder constructs an object step by step.
- Adapter makes an existing object fit an interface it was never built for.
Factory and Builder are about creating. Adapter is about connecting. Different job entirely.
One line to remember
Adapter = the travel plug. Don't modify the charger, don't rewire the wall — put a translator between them.
Part 4 of a series on must-know design patterns in C#, explained the simple way. Earlier parts covered Singleton, Factory Method, and Builder. Next up: Decorator — the gift-wrapping pattern.
Top comments (0)