DPDesign Pattern Quest
#06 / 23
S構造DIFF

アダプタ

Adapter

海外旅行のコンセント変換プラグ。形が合わない者同士を、それぞれ手を加えずにつなぐ。

Intent · 目的

既存クラスのインタフェースを、利用側が期待する別のインタフェースに変換する。元のクラスにも、利用側にも、手を加えずに『間に挟む』だけで動かす ── そういう実用優先のパターン。

Motivation · 動機

海外のホテルでスマホを充電したい。プラグの形が違うから、刺さらない。スマホを改造する? コンセントを工事する? どちらもダメ。間に変換アダプタを噛ませるだけで解決する。プログラムでも同じ。古いライブラリ・サードパーティ API・レガシーシステムを、自分たちが綺麗に作った抽象 (`Logger` 型など) に揃えたい時、相手を改造する代わりに『変換アダプタ』を1個書く。

適用場面

  • 1外部ライブラリのインタフェースを、自分たちの抽象に揃えたい場合。
  • 2既存クラスを修正できない(バイナリ配布、別チーム所有)が、新しい抽象に組み込みたい場合。
  • 3似て非なる複数の既存クラスを、統一インタフェースで扱いたい場合(旧API・新APIの統合期)。

概念図

サンプルコード

class LegacyLogger {
  writeLog(level: string, text: string){ console.log(`[${level}] ${text}`); }
}

interface Logger {
  info(msg: string): void;
  error(msg: string): void;
}

class LegacyLoggerAdapter implements Logger {
  constructor(private legacy: LegacyLogger) {}
  info(msg: string){ this.legacy.writeLog("INFO", msg); }
  error(msg: string){ this.legacy.writeLog("ERROR", msg); }
}

const logger: Logger = new LegacyLoggerAdapter(new LegacyLogger());
logger.info("起動完了");
Pros · メリット
  • 既存資産を一切触らずに、新しい抽象に組み込める。書き換え禁止のレガシーと相性◎。
  • 外部ライブラリへの依存をアダプタ1か所に閉じ込められるので、差し替え時の影響範囲が小さい。
  • テスト時はアダプタごとスタブに差し替えればいいので、外部依存を切り離しやすい。
Cons · デメリット
  • 間接層が1段増える。スタックトレースを読む時にアダプタ名が見えてうっとうしいことも。
  • 細かい API を大量に変換する場合、アダプタの実装量がバカにならない。
  • アダプタが薄い接続だけの場合は良いが、複雑な変換ロジックを抱え込むと『神アダプタ』化する。

関連パターン

⚔ ready to test

理解度を確かめる時間だ

3 問のクイズで、 このパターンを身体に染み込ませよう。

挑戦する →