概要
Adapter(アダプター)パターンは、
互換性のないインターフェース同士を中継するクラスを用意し、スムーズに連携させる設計パターンである。
既存のライブラリや外部システム、古いAPIを現代的なインターフェースへと変換する用途に最適。
Pythonでは柔軟な動的型付けとDuck Typingにより、特に適用しやすい。
1. なぜAdapterが必要か?
❌ 異なるインターフェースをそのまま使おうとすると冗長な変換が必要
# 想定:.read() を使いたいが .load() しかない
data = legacy_loader.load()
stream = io.BytesIO(data)
→ 呼び出し側が変換処理を背負う構造になる
✅ Adapterを導入し、変換をカプセル化
adapter = LegacyLoaderAdapter(legacy_loader)
data = adapter.read()
→ 呼び出し側は理想的なインターフェースに統一される
2. 基本構造
✅ 想定されるインターフェース(Target)
class Reader:
def read(self):
raise NotImplementedError
✅ 適応対象(Adaptee)
class LegacyLoader:
def load(self):
return "データをロード"
✅ Adapter(中継クラス)
class LegacyLoaderAdapter(Reader):
def __init__(self, adaptee: LegacyLoader):
self.adaptee = adaptee
def read(self):
return self.adaptee.load()
✅ 使用例
legacy = LegacyLoader()
reader = LegacyLoaderAdapter(legacy)
print(reader.read())
出力:
データをロード
3. Python的応用:Duck Typingによる暗黙的Adapter
class FileLikeAdapter:
def __init__(self, string_data):
self._buffer = string_data
def read(self):
return self._buffer
# 通常 read() を期待する関数でも使える
def process(reader):
print(reader.read())
adapter = FileLikeAdapter("適応されたデータ")
process(adapter)
→ Pythonでは抽象クラスを継承せずとも、read() を持つだけで代用可能
4. 実務ユースケース
✅ 外部APIのインターフェース変換(旧バージョン → 新設計)
→ 依存コードに影響を与えずにラップで対応可能
✅ データフォーマットの差異調整(CSV → JSON / XML → Dict)
→ 各形式の入出力を標準インターフェースに揃える
✅ Webサービス間のクライアント仕様差の吸収
→ REST → SDK、gRPC → 関数呼び出し等の接続層整形
✅ テスト用のモッククラス作成時のインターフェース適合
→ 異なる構造を持つオブジェクトをテストに使えるようにする
5. よくある誤用と対策
❌ Adapterが複雑化しすぎる(責務を持ちすぎる)
→ ✅ 単に“インターフェースを揃える”ことだけに注力する
❌ Adapteeの全機能をラップしてしまう
→ ✅ 実際に必要なメソッドのみをラップすることで簡潔性を維持
❌ 呼び出し側で再びAdapteeにアクセスしてしまう
→ ✅ Adapterを経由することを前提とした構造にリファクタリング
結語
Adapterパターンとは、“異なる世界のルールを橋渡しし、統一された文脈へ変換する設計”である。
- インターフェースの不一致を吸収し、整合性のあるアーキテクチャを構築
- 外部依存を内部構造に適応させ、保守性と柔軟性を確保
- Pythonではクラス・関数・Duck Typingの柔軟性により、極めて簡潔に設計可能
Pythonicとは、“外との違いを受け入れつつ、内では一貫性を保つこと”。
Adapterパターンはその調和を、設計の結合点に置くための知恵である。