Introduction
The Adapter Pattern is all about making incompatible interfaces work together without modifying their source code. Think of it like hiring a translator instead of rewriting someone's thoughts in a different language or a power plug adapter that lets one interface "fit" into another without modifying either.
Intent & Motivation
- Intent: Convert the interface of a class into another interface clients expect.
- Use case: Often used when integrating legacy systems or external libraries.
Real-world analogy: Using a USB-C to USB-A adapter to connect modern devices to old laptops.
Real-World Use Cases
Category | Use Case | Description |
---|---|---|
Legacy System Integration | Legacy Payment Gateway | Adapter enables a new app to interface with an old gateway without modifying its API. |
Legacy Database Access Layer | Connects a legacy database layer to a modern ORM by translating calls and responses. | |
Expose SOAP via REST | Wraps SOAP endpoints in a REST interface for modern application compatibility. | |
Microsoft Dynamics ↔ SAP via BTP | Adapter layer translates data formats and API calls, enabling seamless communication. | |
API and Format Bridging | Third-party API Integration | Adapts an external API with incompatible data formats to match internal expectations. |
XML ↔ JSON Translation | Adapter converts XML service responses into JSON to maintain frontend compatibility. | |
Hardware & Physical Analogies | USB-C to USB-A Adapter | Physical adapter enables incompatible ports to connect—like classes with mismatched interfaces. |
Universal Remote Control | Translates commands for different devices—similar to adapting method calls in code. | |
Enterprise Software Abstractions | Logging Framework Adapter | Unifies logging calls across diverse libraries with differing interfaces. |
Vendor SDK Wrappers | Encapsulates SDK functionality with a consistent internal interface for maintainability. |
Code Example
Scenario: Adapting a legacy printer class to a new printer interface.
// Target interface
public interface IPrinter
{
void Print(string content);
}
// Adaptee
public class LegacyPrinter
{
public void PrintText(string text)
{
Console.WriteLine("Legacy Printer Output: " + text);
}
}
// Adapter
public class PrinterAdapter : IPrinter
{
private readonly LegacyPrinter _legacyPrinter;
public PrinterAdapter(LegacyPrinter legacyPrinter)
{
_legacyPrinter = legacyPrinter;
}
public void Print(string content)
{
_legacyPrinter.PrintText(content);
}
}
// Client
public class ReportService
{
private readonly IPrinter _printer;
public ReportService(IPrinter printer)
{
_printer = printer;
}
public void Generate()
{
_printer.Print("Monthly Report Content");
}
}
# Target interface
class Printer:
def print(self, content):
raise NotImplementedError
# Adaptee
class LegacyPrinter:
def print_text(self, text):
print(f"Legacy Printer Output: {text}")
# Adapter
class PrinterAdapter(Printer):
def __init__(self, legacy_printer):
self.legacy_printer = legacy_printer
def print(self, content):
self.legacy_printer.print_text(content)
# Client
class ReportService:
def __init__(self, printer):
self.printer = printer
def generate(self):
self.printer.print("Monthly Report Content")
UML Diagram: Adapter Pattern – Legacy Printer Example

This UML diagram illustrates how the Adapter Pattern is used to integrate a legacy printing system into a new architecture using a common interface.
đź§© Components
- IPrinter (Interface)
Defines a commonColorPrint()
andBlackAndWhitePrint()
method that all printers must implement. This is the Target Interface expected by the client. - NewPrinter (Concrete Class)
ImplementsIPrinter
directly. Represents a modern, compatible printer. - LegacyPrinter (Adaptee)
An existing legacry printer that supports onlyBlackAndWhitePrint()
. It is not compatible withIPrinter
. - LegacyPrinterAdaptor (Adapter)
ImplementsIPrinter
and internally uses aLegacyPrinter
instance. TranslatesColorPrint()
toBlackAndWhitePrint()
to bridge compatibility.
🔄 Flow
- The client uses the
IPrinter
interface for loose coupling. - NewPrinter and LegacyPrinterAdaptor are interchangeable from the client’s perspective.
- The adapter makes the LegacyPrinter usable without modifying its code.
When to Use the Adapter Pattern
Situation | Adapter Benefit |
---|---|
Legacy code | Wrap old APIs to fit new interface |
Vendor SDKs | Align different library interfaces |
Microservices | Translate between incompatible contracts |
Pros and Cons
âś… Pros:
- Promotes reuse of existing code
- Encapsulates changes in a single class
- Decouples systems from legacy interfaces
❌ Cons:
- Can add unnecessary layers if overused
- May hide complexity that should be refactored
Quiz
Test your understanding of the Adapter Pattern:
Conclusion
The Adapter Pattern is a powerful tool for bridging gaps between old and new systems. It allows systems to evolve without breaking existing code and is especially useful when working with third-party APIs or legacy code.
Next up in the series: Decorator Pattern – Learn how to dynamically extend behavior without altering the original class.