はじめに
本記事は、GoFデザインパターン解説シリーズの1つです。
今回は、構造に関するパターンの中のFacade(ファサード)パターンを解説します。
3つの構造パターン(Adapter・Bridge・Composite)と比べても、最もシンプルで導入しやすいです。
解決したい課題
複雑なシステムやライブラリを利用するとき、内部にある多数のクラスの、正しい呼び出し順序や依存関係を、利用側がすべて把握しなければならないという問題があります。
例えば、「動画を変換してアップロードする」処理を考えます。
実際には「動画ファイルの読み込み」「コーデックの選択」「圧縮処理」「サムネイル生成」「アップロード先への接続」「アップロード実行」のように、複数のサブシステム(クラス)が絡み合っています。
利用側のコードでこれらすべてを直接呼び出すと、利用側が内部の実装詳細に強く依存してしまい、サブシステムの内部構造が変わるたびに、利用側のコードも修正が必要になります。
Facadeパターンは、複雑なサブシステム群の手前に、シンプルな窓口(ファサード)となるクラスを1つ用意することで、この問題を解決します。
「ファサード(Facade)」とは、建物の「正面の外壁」を意味する言葉で、内部の複雑な構造を隠す「見た目の窓口」を表しています。
クラス構成
- Facade(ファサード): サブシステム群への、シンプルな窓口となるクラス
- Subsystem classes(サブシステムのクラス群): 実際の処理を行う、複数の複雑なクラス群(Facadeパターン導入のために新しく作る必要はなく、既存のクラスがそのまま該当することが多い)
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 + " の変換・アップロードが完了しました");
}
}
利用側のコード
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版 [ 結城 浩 ]
※本リンクはアフィリエイトリンクを含みます。


