概要
Adapter(アダプター)パターンは、
互換性のないインターフェース同士を繋ぐ“変換層”を設けることで、
既存コードと新コードをシームレスに連携させる構造パターンである。
Pythonの柔軟な型システムとダックタイピングにより、
このパターンは非常に実用性が高く、既存ライブラリの置き換えや外部モジュールの統合に多用される。
1. なぜAdapterが必要か?
❌ 既存コードのインターフェースに依存した構造
class LegacyPrinter:
def print_text(self, txt):
print(txt)
def render(printer):
printer.print(txt="Hello") # エラー!print()なんてない
→ 既存コードを壊さずに、新しい仕様へ適合させたい
✅ Adapterでインターフェースの橋渡しを行う
class PrinterAdapter:
def __init__(self, legacy):
self.legacy = legacy
def print(self, txt):
self.legacy.print_text(txt)
→ インターフェースの変換により、互換性を確保
2. 基本構造
✅ 旧API(適合対象)
class LegacyAudioPlayer:
def play_mp3(self, filename):
print(f"Playing MP3: {filename}")
✅ 新API(期待されるインターフェース)
class AudioPlayer:
def play(self, filename):
raise NotImplementedError
✅ Adapterクラス
class Mp3Adapter(AudioPlayer):
def __init__(self, legacy_player):
self.legacy = legacy_player
def play(self, filename):
self.legacy.play_mp3(filename)
✅ 使用例
legacy = LegacyAudioPlayer()
player = Mp3Adapter(legacy)
player.play("song.mp3") # → Playing MP3: song.mp3
3. Python的応用:関数アダプタ、ラッパー関数
✅ 構造体の属性名変更に対して関数で対応
def adapter_wrapper(obj):
return {
"name": obj.full_name,
"email": obj.mail_address
}
✅ 外部ライブラリのクラスをラップする
from external_lib import ComplexParser
class SimpleParser:
def __init__(self):
self.parser = ComplexParser()
def parse(self, content):
return self.parser.process(content) # 外部のmethod名を抽象化
4. 実務ユースケース
✅ 外部APIのクライアントライブラリ統一
→ requests
, urllib3
, httpx
のような異なるHTTPクライアントの統一インターフェース化
✅ データ形式変換(JSON, XML, YAML)
→ 既存クラスの出力を別形式に変換するためのデコレーター・アダプタ設計
✅ モックと実クラスの橋渡し
→ テスト時はAdapter経由でMockを注入、本番時は実クラスに差し替え可能
5. よくある誤用と対策
❌ Adapterが処理ロジックまで持ち始める
→ ✅ アダプタはあくまで“橋渡し”に徹する。ビジネスロジックは委譲先に任せる
❌ 利用者がAdapterを意識しすぎてしまう
→ ✅ 抽象インターフェースを介して実装の差異を意識させない設計に
❌ AdapterとDecoratorの混同
→ ✅ Adapterはインターフェース変換、Decoratorは機能の付加
→ 目的が異なることを理解して使い分ける
結語
Adapterパターンとは、“インターフェースの不一致を吸収し、設計の整合性を保つための変換レイヤー”である。
- 過去資産や外部ライブラリと、現在のコードベースの橋渡し役
- 抽象化と疎結合を実現し、拡張・置換・テストの柔軟性を大幅に向上
- Pythonの構文的柔軟性により、アダプタ実装は極めてシンプルかつ強力
Pythonicとは、“差異を受け入れ、意図を揃える設計”であり、
Adapterパターンはその整合性と互換性を、構造として確保する技法である。