#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 問のクイズで、 このパターンを身体に染み込ませよう。