TechCraft – エンジニアのためのスキルアップメモ

エンジニアのスキルアップを少しでも加速する技術ブログ

デザインパターン 第3回 (Adapter, Decorator, Facade)

GoFGang of Four)のデザインパターン:構造パターン(Adapter, Decorator, Facade)を徹底解説

1. はじめに

オブジェクト指向プログラミングにおいて、「異なるクラスをどのように組み合わせるか?」 は重要な課題です。
特に、クラス間の結合度を最小限に抑えながら、柔軟に機能を拡張 できる設計が求められます。

この問題を解決するために、GoFGang 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)」を解説します!

参考資料

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Java言語で学ぶデザインパターン入門第3版 [ 結城 浩 ]
価格:4,290円(税込、送料無料) (2025/3/7時点)


バランスよく書かれている。


網羅的に書かれています。骨太。


C++デザインパターン。少し高難度。