🧩 Adapter Pattern
✅ Intent
- Create a bridge between an expected interface and an existing implementation when they do not match
- Introduce a conversion layer (Adapter) to decouple the caller from the incompatible implementation
✅ Motivation
- Avoid tight coupling with legacy APIs by exposing a unified interface to the caller
- Make it easy to swap in other implementations (e.g., mocks, alternative APIs) in the future
✅ When to Use
- When integrating with external services that expose incompatible APIs
- When maintaining a consistent internal interface across your codebase
- When testing or replacing dependencies with mocks is important
✅ Code Example
- TypeScript
- PHP
- Python
// レガシーAPI(旧仕様)
class LegacyPrinter {
printText(text: string): void {
console.log(`*** ${text} ***`);
}
}
// 新しいインターフェース
interface Printer {
print(text: string): void;
}
// Adapter(旧APIをラップ)
class LegacyPrinterAdapter implements Printer {
private legacy: LegacyPrinter;
constructor() {
this.legacy = new LegacyPrinter();
}
print(text: string): void {
const formatted = text.toUpperCase(); // 必要な変換をここで吸収
this.legacy.printText(formatted);
}
}
// 新しいコード(Adapter に依存)
class ReportGenerator {
constructor(private printer: Printer) {}
printSummary(text: string): void {
this.printer.print(text);
}
}
// 利用例
const printer = new LegacyPrinterAdapter();
const report = new ReportGenerator(printer);
report.printSummary("月次レポート");
<?php
// レガシーAPI(旧仕様)
class LegacyPrinter {
public function printText(string $text): void {
echo "*** {$text} ***\n";
}
}
// 新しいインターフェース
interface Printer {
public function print(string $text): void;
}
// Adapter(旧APIをラップ)
class LegacyPrinterAdapter implements Printer {
private LegacyPrinter $legacy;
public function __construct() {
$this->legacy = new LegacyPrinter();
}
public function print(string $text): void {
$formatted = strtoupper($text); // 変換はここで
$this->legacy->printText($formatted);
}
}
// 新しいコード
class ReportGenerator {
public function __construct(private Printer $printer) {}
public function printSummary(string $text): void {
$this->printer->print($text);
}
}
// 利用例
$printer = new LegacyPrinterAdapter();
$report = new ReportGenerator($printer);
$report->printSummary("月次レポート");
from abc import ABC, abstractmethod
# レガシーAPI(旧仕様)
class LegacyPrinter:
def print_text(self, text: str):
print(f"*** {text} ***")
# 新しいインターフェース
class Printer(ABC):
@abstractmethod
def print(self, text: str):
pass
# Adapter(旧APIをラップ)
class LegacyPrinterAdapter(Printer):
def __init__(self):
self.legacy = LegacyPrinter()
def print(self, text: str):
formatted = text.upper()
self.legacy.print_text(formatted)
# 新しいコード
class ReportGenerator:
def __init__(self, printer: Printer):
self.printer = printer
def print_summary(self, text: str):
self.printer.print(text)
# 利用例
printer = LegacyPrinterAdapter()
report = ReportGenerator(printer)
report.print_summary("月次レポート")
✅ Explanation
This code uses the Adapter
pattern to connect two incompatible interfaces:
a legacy implementation (LegacyPrinter
) and a new expected interface (Printer
).
The ReportGenerator
uses only the new interface and doesn't need to know the details of the legacy class.
The Adapter
pattern allows reusing legacy classes without modification by adapting them to a new interface.
1. Overview of the Adapter Pattern
-
Target: The interface expected by the client
- Represented by
Printer
- Represented by
-
Adaptee: The existing class with an incompatible interface
- Represented by
LegacyPrinter
- Represented by
-
Adapter: Wraps the
Adaptee
and implements theTarget
interface- Represented by
LegacyPrinterAdapter
- Represented by
-
Client: Relies on the
Target
interface- Represented by
ReportGenerator
- Represented by
2. Key Classes and Their Roles
-
LegacyPrinter
- The existing legacy class (
Adaptee
) - Provides a
printText
method
- The existing legacy class (
-
Printer
- The new interface (
Target
) - Defines a
print
method
- The new interface (
-
LegacyPrinterAdapter
- Implements the
Printer
interface and wraps aLegacyPrinter
instance - Translates method calls as needed (e.g., converting text to uppercase)
- Implements the
-
ReportGenerator
- The client class
- Depends only on the
Printer
interface - Uses
printSummary
to generate and print output
3. UML Class Diagram
4. Benefits of the Adapter Pattern
- Provides Compatibility: Adapts existing classes to new interfaces without modifying their code
- Improves Reusability: Legacy classes can be reused in new code
- Reduces Coupling: Clients depend only on the new interface, not the legacy implementation
This design is especially useful when integrating legacy systems into modern applications,
enhancing flexibility and maintainability without invasive changes to existing code.