🧩 Adapter パターン
✅ 設計意図
- 「期待されているインターフェース」と「実際の実装」が異なる場合に、橋渡し役を作る
- 呼び出し元と実装の間に**変換レイヤー(Adapter)**を挟むことで、疎結合にする
✅ 適用理由
- 旧 API の仕様に依存せず、呼び出し元は統一されたインターフェースを使える
- 将来的に他の実装(モック、別の API)へ差し替えが容易になる
✅ 向いているシーン
- 外部サービスの仕様が異なる
- 自前のコードベースに統一インターフェースを維持したい
- テスト容易性や交換可能性が求められる場面
✅ コード例
- 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("月次レポート")
✅ 解説
このコードは Adapter
パターン を使用して、互換性のないインターフェース(LegacyPrinter
と Printer
)をつなぎ、
新しいコード(ReportGenerator
)が旧仕様のクラスを利用できるようにする設計を実現している。
Adapter
パターンは、既存のクラスを変更せずに、新しいインターフェースに適合させるデザインパターン。
1. Adapter パターンの概要
- Target: クライアントが期待するインターフェース
- このコードでは
Printer
が該当
- このコードでは
- Adaptee: 既存のクラスで、新しいインターフェースに適合させる対象
- このコードでは
LegacyPrinter
が該当
- このコードでは
- Adapter:
Adaptee
をラップし、Target
インターフェースを実装するクラス。- このコードでは
LegacyPrinterAdapter
が該当
- このコードでは
- Client:
Target
インターフェースを使用するクラス- このコードでは
ReportGenerator
が該当
- このコードでは
2. 主なクラスとその役割
LegacyPrinter
- 旧仕様のクラス(
Adaptee
) printText
メソッドでテキストを出力
- 旧仕様のクラス(
Printer
- 新しいインターフェース(
Target
) print
メソッドを定義
- 新しいインターフェース(
LegacyPrinterAdapter
- Adapter クラス
Printer
を実装し、LegacyPrinter
をラップ- 必要な変換(例: テキストを大文字に変換)を吸収
ReportGenerator
- クライアントクラス
Printer
インターフェースに依存し、printSummary
メソッドでテキストを出力
3. UML クラス図
4. Adapter パターンの利点
- 互換性の提供: 既存のクラスを変更せずに、新しいインターフェースに適合可能
- 再利用性: 旧仕様のクラス(
LegacyPrinter
)を新しいコードで再利用可能 - 疎結合: クライアント(
ReportGenerator
)は新しいインターフェース(Printer
)に依存するため、旧仕様のクラスに直接依存しない
この設計は、既存のコードを変更せずに新しいシステムに統合する必要がある場面で非常に有効であり、コードの柔軟性と保守性を向上させる。