0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

本記事は、GoFデザインパターン解説シリーズの1つです。

今回は、構造に関するパターンの中のFacade(ファサード)パターンを解説します。

3つの構造パターン(Adapter・Bridge・Composite)と比べても、最もシンプルで導入しやすいです。

解決したい課題

複雑なシステムやライブラリを利用するとき、内部にある多数のクラスの、正しい呼び出し順序や依存関係を、利用側がすべて把握しなければならないという問題があります。

例えば、「動画を変換してアップロードする」処理を考えます。

実際には「動画ファイルの読み込み」「コーデックの選択」「圧縮処理」「サムネイル生成」「アップロード先への接続」「アップロード実行」のように、複数のサブシステム(クラス)が絡み合っています。

利用側のコードでこれらすべてを直接呼び出すと、利用側が内部の実装詳細に強く依存してしまい、サブシステムの内部構造が変わるたびに、利用側のコードも修正が必要になります。

Facadeパターンは、複雑なサブシステム群の手前に、シンプルな窓口(ファサード)となるクラスを1つ用意することで、この問題を解決します。

「ファサード(Facade)」とは、建物の「正面の外壁」を意味する言葉で、内部の複雑な構造を隠す「見た目の窓口」を表しています。

image.png

クラス構成

  • Facade(ファサード): サブシステム群への、シンプルな窓口となるクラス
  • Subsystem classes(サブシステムのクラス群): 実際の処理を行う、複数の複雑なクラス群(Facadeパターン導入のために新しく作る必要はなく、既存のクラスがそのまま該当することが多い)

image.png

Javaでの実装例

動画変換・アップロード処理を例に実装してみます。

Subsystem classes:複雑な内部処理を担う、複数のクラス

public class VideoFile {
    private final String filename;

    public VideoFile(String filename) {
        this.filename = filename;
        System.out.println("[VideoFile] " + filename + " を読み込みました");
    }

    public String getFilename() {
        return filename;
    }
}

public class CodecFactory {
    public String extractCodec(VideoFile file) {
        System.out.println("[CodecFactory] " + file.getFilename() + " のコーデックを解析中...");
        return "H.264";
    }
}

public class VideoCompressor {
    public void compress(VideoFile file, String codec) {
        System.out.println("[VideoCompressor] " + codec + "形式で圧縮中...");
    }
}

public class ThumbnailGenerator {
    public void generate(VideoFile file) {
        System.out.println("[ThumbnailGenerator] " + file.getFilename() + " のサムネイルを生成中...");
    }
}

public class CloudUploader {
    public void connect() {
        System.out.println("[CloudUploader] クラウドストレージへ接続中...");
    }

    public void upload(VideoFile file) {
        System.out.println("[CloudUploader] " + file.getFilename() + " をアップロード中...");
    }
}

Facade:シンプルな窓口クラス

public class VideoConversionFacade {
    private final CodecFactory codecFactory = new CodecFactory();
    private final VideoCompressor compressor = new VideoCompressor();
    private final ThumbnailGenerator thumbnailGenerator = new ThumbnailGenerator();
    private final CloudUploader uploader = new CloudUploader();

    // サブシステム群の複雑な呼び出し順序を、このメソッド1つに集約する
    public void convertAndUpload(String filename) {
        VideoFile file = new VideoFile(filename);

        String codec = codecFactory.extractCodec(file);
        compressor.compress(file, codec);
        thumbnailGenerator.generate(file);

        uploader.connect();
        uploader.upload(file);

        System.out.println("[VideoConversionFacade] " + filename + " の変換・アップロードが完了しました");
    }
}

image.png

利用側のコード

public class Main {
    public static void main(String[] args) {
        VideoConversionFacade facade = new VideoConversionFacade();

        // 利用側は、内部の6クラス・呼び出し順序を一切意識せず、メソッド1つ呼ぶだけでよい
        facade.convertAndUpload("holiday_trip.mp4");
    }
}

実行結果:

[VideoFile] holiday_trip.mp4 を読み込みました
[CodecFactory] holiday_trip.mp4 のコーデックを解析中...
[VideoCompressor] H.264形式で圧縮中...
[ThumbnailGenerator] holiday_trip.mp4 のサムネイルを生成中...
[CloudUploader] クラウドストレージへ接続中...
[CloudUploader] holiday_trip.mp4 をアップロード中...
[VideoConversionFacade] holiday_trip.mp4 の変換・アップロードが完了しました

利用側のコードは、VideoFile・CodecFactory・VideoCompressorなど6つのクラスの存在や呼び出し順序を一切知る必要がなく、VideoConversionFacade#convertAndUpload()を呼ぶだけで済んでいます。

サブシステム内部の処理順序が変わったり、新しいステップ(例えばウイルススキャン)が追加されたりしても、Facadeクラスの内部だけを修正すればよく、利用側のコードは変更不要です。

注意点:Facadeは「隠蔽」であって「制限」ではない

Facadeパターンを導入しても、サブシステムのクラス群(VideoFileやCodecFactory等)自体は、引き続き外部から直接利用可能な状態のままです。

Facadeは「複雑な処理をまとめて簡単に呼べる窓口を追加する」ものであり、「サブシステムへの直接アクセスを禁止する」ものではありません。

細かい制御が必要な一部の利用者は、引き続きサブシステムを直接操作することもできます。

使用場面

  • 複数の複雑なクラス・ライブラリを組み合わせて使う処理を、シンプルなAPIとして提供したい場合
  • レイヤードアーキテクチャにおいて、あるレイヤー(例: プレゼンテーション層)から、別のレイヤー(例: 複数のサービスクラスからなるビジネスロジック層)への窓口を1つに整理したい場合
  • 外部ライブラリの複雑な初期化・設定処理をカプセル化し、自社アプリ内で簡単に呼び出せるようにしたい場合

まとめ

項目 内容
解決する課題 複雑なサブシステム群を、利用側が呼び出し順序まで含めて把握しなければならない
実現方法 サブシステム群への処理をまとめて呼び出す、シンプルな窓口(Facade)クラスを用意する
メリット 利用側は内部の複雑さを意識せずに済み、サブシステム内部の変更も利用側に影響しにくくなる
注意点 サブシステムへの直接アクセスを禁止するものではない(あくまで簡易な窓口の追加)

おすすめ書籍

GoFの23パターンをより深く学びたい方には、以下の書籍がおすすめです。
Java言語のサンプルコードとUMLを使い、初心者にもわかりやすく解説されています。

Java言語で学ぶデザインパターン入門 第3版 [ 結城 浩 ]

※本リンクはアフィリエイトリンクを含みます。

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?