<一覧に戻る

アダプタパターン(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、レガシーコード、ライブラリ移行など、実務での出番はとても多いもの。もし「インターフェースが合わない…」と感じたら、まずはアダプタで“形を合わせられないか”を検討してみましょう。きっと、コードの変更範囲を小さく、影響を局所化できるはずです。

小さく試して、少しずつ慣れていけば大丈夫です。次に既存コードと新しいコードをつなぐ場面が来たとき、あなたは迷わず「アダプタを作ろう」と判断できるようになります。

出力結果: