ラッピングパターン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点です。インターフェースを変えるのか、隠すのか、制御するのか、追加するのか。
この問いがあれば、コードの前でもう迷いません。今日の比較表を保存して、必要なときに取り出して使ってください。

