Section 5 / 10
アダプタパターン(Adapter Pattern)
「使いたいライブラリがあるけれど、メソッド名やデータの形が自分のコードと合わない…」そんな経験はありませんか?
アダプタパターンは、まさにこの“かみ合わない”を解決するためのデザインパターンです。
互換性のないインターフェース同士をつなぎ、既存コードをほとんど変えずに再利用できるようにします。 Pythonの初心者でもイメージしやすいように、日常の「電源プラグ変換」を例にして、わかりやすく解説していきます。
アダプタパターンとは?直感的なイメージから
最初にイメージをつかみましょう。日本の家電は100V、ヨーロッパのコンセントは230Vです。差し込み口や電圧が違うとそのままでは使えませんよね。 そこで登場するのが「変換プラグ(アダプタ)」。アダプタを間に挟むと、異なる規格同士でも安全に接続できます。
プログラミングでも同じことが起きます。クライアント(使う側)が期待するインターフェース(メソッドやデータ形式)と、既存のクラス(使われる側)のインターフェースが合わない。その“ズレ”を埋めるのがアダプタパターンです。
アダプタパターンの目的を把握しよう
いきなり細かい話に入る前に、どんな場面で役立つのか、ゴールを明確にしておきましょう。 アダプタパターンの主な目的は次のとおりです。
- 既存のクラスを変更せずに異なるインターフェースを持つクラスを連携させる
- クライアント側の変更を最小限に抑え、影響範囲をコントロールする
- 重複実装を避けて、再利用性と保守性を高める
「外部APIの仕様が変わった」「レガシーコードを新設計に組み込みたい」など、現実の開発でよくある悩みに効くのがアダプタパターンです。
アダプタパターンの構成要素
アダプタパターンの構成要素を知ると、コードがグッと読みやすくなります。ここでは、難しい言葉を身近な言い換えと一緒に紹介します。
- ターゲット(Target):クライアントが“こういう形で呼びたい”と期待しているインターフェース。例えるなら「ヨーロッパ用の230Vコンセント」。
- アダプティ(Adaptee):すでに存在しているけれど、形が合わない側。たとえば「日本の100Vコンセント」。
- アダプタ(Adapter):両者の橋渡し役。ターゲットの顔をして、裏側でアダプティを呼び出し、形を合わせます。
サンプルコード:電圧変換のイメージで学ぶ
ここからは、Pythonで動くサンプルを通して具体的に理解していきましょう。 今回は「100Vしか出せない既存クラス」を「230Vが欲しいクライアント」に接続するアダプタを作ります。電気的な正確さではなく、あくまで“形を合わせる”イメージに注目してください。
class EuropePowerSource:
"""ターゲット: クライアントが期待するインターフェース(230Vを出力)"""
def output_230v(self) -> int:
raise NotImplementedError
class JapanOutlet:
"""アダプティ: 既存のインターフェース(100Vを出力)"""
def output_100v(self) -> int:
return 100
class JapanToEuropeAdapter(EuropePowerSource):
"""アダプタ: EuropePowerSourceの顔でJapanOutletを内部で利用する"""
def __init__(self, outlet: JapanOutlet) -> None:
self._outlet = outlet
def output_230v(self) -> int:
# 実際の電圧変換ではありません。学習のための簡略化です。
# 100V -> 230V へ変換した「ことにする」。
base = self._outlet.output_100v()
return int(base * 2.3) # 100 * 2.3 = 230
def use_eu_device(source: EuropePowerSource) -> None:
"""クライアント: 230Vを出せる“誰か”にだけ依存"""
volts = source.output_230v()
print(f"230V対応機器を起動します… 受電: {volts}V")
# 実行例
jp_outlet = JapanOutlet() # 既存(100V)
adapter = JapanToEuropeAdapter(jp_outlet) # 変換アダプタ
use_eu_device(adapter) # クライアントは230Vだけを意識
最初に、クライアントが欲しがっているのは「230Vを出せる電源(EuropePowerSource)」です。しかし手元にあるのは「100Vしか出せない日本のコンセント(JapanOutlet)」。
このままでは差し込めません。そこで変換アダプタ(JapanToEuropeAdapter)を作り、表向きはEuropePowerSourceとして振る舞いながら、裏側ではJapanOutletを呼び出します。
クライアント関数use_eu_deviceはとても素直です。「230Vを出せる相手」さえもらえれば満足。相手が本当にヨーロッパのコンセントなのか、変換アダプタなのか、はたまた他の何かなのかは気にしません。これが依存を減らす設計の力です。
いつ使うの?具体的なシーンをイメージしよう
現場で「これ、アダプタパターンが使えるかも」と感じる瞬間は、次のようなときです。思い当たるものはありますか?
- 外部APIやサードパーティのライブラリのメソッド名・引数・返り値が自分のコードと合わない
- レガシーコードを新しい設計に徐々に組み込みたいが、全面改修は避けたい
- テストのためにインターフェースだけを合わせたモックやスタブを差し替えたい
- データ形式(例: dict とオブジェクト、JSONとPythonオブジェクト)の“形”を合わせたい
アダプタパターンのメリットを整理
ここまでの流れを踏まえて、得られるメリットを改めて押さえておきましょう。
- 既存コードを変更せずに再利用できるため、リスクと工数を抑えられる
- クライアントは「期待する形」だけに依存するので、結合度が下がり保守が楽になる
- インターフェース変換を一箇所(アダプタ)に集約でき、影響範囲をコントロールしやすい
まとめ:迷ったら“間に挟む”を思い出す
アダプタパターンは、互換性のないインターフェース同士を「間に挟んで」つなぐための基本テクニックです。
クライアントが期待する形を守りながら、既存資産を安全に再利用できます。外部API、レガシーコード、ライブラリ移行など、実務での出番はとても多いもの。もし「インターフェースが合わない…」と感じたら、まずはアダプタで“形を合わせられないか”を検討してみましょう。きっと、コードの変更範囲を小さく、影響を局所化できるはずです。
小さく試して、少しずつ慣れていけば大丈夫です。次に既存コードと新しいコードをつなぐ場面が来たとき、あなたは迷わず「アダプタを作ろう」と判断できるようになります。