ソフトウェア設計

Adapter・Facade・Proxy・Decoratorの違い

Adapterはインターフェースを変換し、Facadeは複雑さを隠し、Proxyはアクセスを制御し、Decoratorは機能を追加します。構造が似た4つのラッピングパターンを、目的と選択基準から比較します。

読了 6 分
Adapter・Facade・Proxy・Decoratorの違いのカバー画像

ラッピングパターン4兄弟は、なぜこんなに紛らわしいのか

デザインパターンを学んでいると、必ず壁にぶつかるポイントがあります。

Adapter、Facade、Proxy、Decorator。この4つは混同しやすく、区別に迷いがちです。

「どれも別のオブジェクトをラップしているだけでは?」と思うのも無理はありません。実際、4つとも内部に別のオブジェクトを持ち、その前に立つ構造なので、見た目がよく似ています。

そのため、名前は覚えていても、コード上でどれが何なのか区別できないことがよくあります。

まず、核心を押さえましょう。

4つのパターンはすべてオブジェクトをラップしますが、ラップする「目的」が異なります。

Adapterはインターフェースを変え、Facadeは複雑さを隠し、Proxyはアクセスを制御し、Decoratorは機能を追加します。

この一文を押さえておけば、あとは自然についてきます。この記事では、4兄弟をはっきり整理し、コードで迷わないようにします。

比較のあとに実装例が必要なら、SwiftのAdapterでレガシーAPIをラップするSwiftのFacadeでサブシステムを単純化するも続けて読めます。


4兄弟をひと目で見分ける比較表

文章で説明する前に、まず表を見てみましょう。私は迷ったときに見るため、この表を机の前に貼っていました。

パターン 目的 インターフェース ラップする対象
Adapter 合わないインターフェースを適合させる 変わる 通常1つ
Facade 複雑な複数のサブシステムを単純な窓口にする 新しく作る 複数
Proxy 元のオブジェクトへのアクセスを制御・遅延・キャッシュする 同じ 1つ
Decorator 元のオブジェクトに機能を追加する 同じ 1つ

最も重要な判別基準は「インターフェースがそのままか」です。

ProxyとDecoratorは元のオブジェクトと同じインターフェースを持つため、利用側はラップされていることを意識しなくて済みます。

一方、Adapterは意図的にインターフェースを変え、Facadeはまったく新しい窓口を作ります。

ラップする目的だけを考えれば、違いはこう分かれます
ラップする目的だけを考えれば、違いはこう分かれます

AdapterとDecoratorはどう違うのか

最も混同されやすい組み合わせがこの2つです。コードで見ると違いがはっきりします。

Adapterは「合わないものを合わせる」ための変換プラグです。220Vの機器を110Vのソケットに接続する、あのAdapterです。

// 既存ライブラリには legacyPrint(text:)しかない⟧
// 私たちのコードが draw()を期待するとき⟧
protocol Renderer { func draw() }

struct LegacyLabel { func legacyPrint(_ t: String) { print(t) } }

struct LabelAdapter: Renderer {
    let legacy: LegacyLabel
    func draw() { legacy.legacyPrint("変換された呼び出し") }
}

LabelAdapter(legacy: LegacyLabel()).draw()
// 出力:変換された呼び出し

インターフェースがlegacyPrintからdrawに変わることがポイントです。

Decoratorはインターフェースをそのままにして、機能だけを重ねます。コーヒーにショットを追加するようなものです。

protocol Coffee { func cost() -> Int }

struct Americano: Coffee { func cost() -> Int { 4000 } }

struct ShotDecorator: Coffee {
    let base: Coffee
    func cost() -> Int { base.cost() + 500 }
}

ShotDecorator(base: Americano()).cost()
// 出力: 4500

どちらもbaseを内包しますが、Adapterは名前を変え、Decoratorは機能を豊かにします。目的はまったく異なります。


ProxyとFacadeは?

Proxyは元のオブジェクトとまったく同じインターフェースを持ちます。その代わり、途中で制御を行います。

画像の読み込みなら、本物の画像をすぐには読み込まず、必要になるまで遅らせます。これが遅延ロードです。

キャッシュ、権限チェック、ロギングもProxyがよく担う役割です。利用側は元のオブジェクトを呼んでいるつもりでも、実際には代理人を呼んでいます。

Facadeは少し性質が違います。1つをラップするのではなく、複数のものを背後に隠します。

ホームシアターなら、プロジェクターとスピーカーを起動し、照明を落として再生する一連の処理を、watchMovie() という1つの呼び出しにまとめます。内部を知らなくても、ボタン1つで使えるようにするわけです。

迷ったときに見るため、私はこのメモをモニターに貼っていました
迷ったときに見るため、私はこのメモをモニターに貼っていました

まとめると、Proxyは「同じドアの前に立つ門番」、Facadeは「複数のドアを代わりに開ける案内デスク」です。


いつ使い、いつ避けるべきか

パターンは道具なので、状況に合わせて使うべきです。無理に当てはめると、かえってコードが複雑になります。

状況 判断
外部ライブラリのインターフェースが合わない Adapter
複雑な複数モジュールを簡単な窓口から使いたい Facade
アクセス制御・遅延ロード・キャッシュが必要 Proxy
機能を組み合わせ、柔軟に追加したい Decorator

避けるべき場面も明確です。

  • ラップする理由がないのに「パターンを使った」という理由だけで入れると、階層が増えるだけです。
  • Decoratorを重ねすぎると、デバッグ時にどこで値が変わったのか追跡できず、地獄になります。
  • Facadeが多くを抱え込みすぎると、それ自体が巨大な塊になります。

私は「今ラップする目的は、4つのうちどれか?」と自問し、答えられなければパターンを外します。

面接ではこう聞かれます

Q. ProxyとDecoratorはどちらも同じインターフェースでオブジェクトをラップしますが、何が違いますか?

A. 目的が異なります。Proxyは元のオブジェクトへのアクセスを制御・遅延・キャッシュすることが目的で、元の機能自体は変えません。Decoratorは元のインターフェースを維持したまま機能を追加します。Proxyは「呼ぶかどうか」を決め、Decoratorは「呼んだあとに何を追加するか」を決める、と考えると分かりやすいでしょう。

Q. AdapterとFacadeの違いは何ですか?

A. Adapterは通常、1つの対象をラップして合わないインターフェースを適合させます。Facadeは複数のサブシステムをラップし、単純な新しい窓口を提供します。Adapterはインターフェースを「翻訳」し、Facadeはインターフェースを「要約」すると理解するとよいでしょう。


まとめ

4兄弟を分けるのは、結局「なぜラップするのか」という1点です。インターフェースを変えるのか、隠すのか、制御するのか、追加するのか。

この問いがあれば、コードの前でもう迷いません。今日の比較表を保存して、必要なときに取り出して使ってください。

あわせて読みたい