GoF(Gang of Four)のデザインパターン:構造パターン(Adapter, Decorator, Facade)を徹底解説
1. はじめに
オブジェクト指向プログラミングにおいて、「異なるクラスをどのように組み合わせるか?」 は重要な課題です。
特に、クラス間の結合度を最小限に抑えながら、柔軟に機能を拡張 できる設計が求められます。
この問題を解決するために、GoF(Gang of Four)が提唱した構造パターン(Structural Patterns) が役立ちます。
構造パターンは、オブジェクトやクラスをどのように組み合わせて、システム全体の構造を整理するか を定義します。
本記事では、代表的な3つの構造パターン(Adapter、Decorator、Facade)の概要と実装(複数言語) を紹介します。
2. 構造パターンとは?
GoFのデザインパターンの中で、構造パターンは以下のように分類されます。
パターン | 概要 |
---|---|
Adapter | 互換性のないインターフェースを統一する |
Decorator | 既存のオブジェクトに新しい機能を動的に追加する |
Facade | システムの複雑な構造を隠し、シンプルなインターフェースを提供する |
Bridge | 実装と抽象を分離し、拡張しやすい設計を提供する |
Composite | オブジェクトのツリー構造を管理する |
Proxy | 直接アクセスせずに代理オブジェクトを通して操作する |
今回は、最も使用頻度の高い Adapter、Decorator、Facade に焦点を当てて解説します。
3. Adapterパターン:異なるインターフェースの橋渡し
3.1. Adapterパターンとは?
Adapter(アダプター)パターン は、異なるインターフェースを持つクラスを統一的に扱えるようにする デザインパターンです。
✅ メリット
- 互換性のないクラス同士を連携可能にする
- 既存のコードを変更せずに統一されたインターフェースを提供できる
❌ デメリット
- 新しいAdapterクラスが必要になるため、コード量が増える
3.2. Adapterパターンの実装
Javaでの実装
// 既存のクラス(互換性のないインターフェース) class OldSystem { public void legacyMethod() { System.out.println("旧システムのメソッド"); } } // 統一したいインターフェース interface NewSystem { void newMethod(); } // Adapterクラス(旧システムを新しいインターフェースに適合) class Adapter implements NewSystem { private OldSystem oldSystem; public Adapter(OldSystem oldSystem) { this.oldSystem = oldSystem; } public void newMethod() { oldSystem.legacyMethod(); } } // 使用例 public class Main { public static void main(String[] args) { OldSystem oldSystem = new OldSystem(); NewSystem adapter = new Adapter(oldSystem); adapter.newMethod(); // Output: 旧システムのメソッド } }
✅ Adapterを介して、新しいインターフェースと旧システムを統一
4. Decoratorパターン:機能を動的に追加
4.1. Decoratorパターンとは?
Decorator(デコレーター)パターン は、既存のクラスに対して、新しい機能を動的に追加できるようにする パターンです。
✅ メリット
- オブジェクトを変更せずに、新しい機能を追加可能
- 柔軟な拡張ができる
❌ デメリット
- デコレーターの組み合わせが複雑になると、管理が難しくなる
4.2. Decoratorパターンの実装
Pythonでの実装
# 基本クラス class Coffee: def cost(self): return 5 # Decoratorクラス class MilkDecorator: def __init__(self, coffee): self.coffee = coffee def cost(self): return self.coffee.cost() + 2 # 牛乳の追加料金 # Decoratorクラス class SugarDecorator: def __init__(self, coffee): self.coffee = coffee def cost(self): return self.coffee.cost() + 1 # 砂糖の追加料金 # 使用例 coffee = Coffee() print("基本のコーヒー:", coffee.cost()) # Output: 5 coffee_with_milk = MilkDecorator(coffee) print("ミルク入りコーヒー:", coffee_with_milk.cost()) # Output: 7 coffee_with_milk_sugar = SugarDecorator(coffee_with_milk) print("ミルク&砂糖入りコーヒー:", coffee_with_milk_sugar.cost()) # Output: 8
✅ オブジェクトを変更せずに、装飾(Decorator)を追加することで機能を拡張できる
5. Facadeパターン:システムの簡易インターフェース
5.1. Facadeパターンとは?
Facade(ファサード)パターン は、複雑なシステムの操作を簡単にするための統一インターフェースを提供する パターンです。
✅ メリット
- システムの内部構造を隠し、クライアント側のコードをシンプルにする
- 変更が容易になり、メンテナンス性が向上する
❌ デメリット
- 内部の詳細な制御が難しくなる
5.2. Facadeパターンの実装
C++での実装
#include <iostream> // サブシステム1 class SubSystemA { public: void operationA() { std::cout << "サブシステムAの処理\n"; } }; // サブシステム2 class SubSystemB { public: void operationB() { std::cout << "サブシステムBの処理\n"; } }; // Facadeクラス(統一されたインターフェース) class Facade { private: SubSystemA a; SubSystemB b; public: void operation() { a.operationA(); b.operationB(); } }; // 使用例 int main() { Facade facade; facade.operation(); return 0; }
✅ クライアント側は Facade
を使うだけで、内部の複雑な処理を意識せずに利用可能
6. まとめ
パターン | 概要 |
---|---|
Adapter | 互換性のないインターフェースを統一する |
Decorator | 既存のオブジェクトに機能を動的に追加する |
Facade | システムの複雑な構造を隠し、簡単なインターフェースを提供する |
構造パターンは、オブジェクト間の関係を整理し、コードの保守性や拡張性を向上させる ために非常に有効です。
📌 次回は「GoFの振る舞いパターン(Observer, Strategy, Template Method)」を解説します!
参考資料
バランスよく書かれている。
網羅的に書かれています。骨太。