ソフトウェアが大きくなると、利用者は複数のクラスやモジュールを正しい順序で呼び出さなければならず、コードが複雑になりがちです。
ファサードパターンを使用することで複雑さを隠し、窓口ひとつで必要な処理をまとめて呼び出すことができ、呼び出し側のコードがシンプルになったり、内部の保守性が高まるといったメリットがあります。
本記事では簡単なサンプルコードを例に、ファサードパターンについて紹介したいと思います。
前提知識
-
C++の基礎文法を理解している
ファサードパターンとは
ファサードパターン/Facade Pattern は、複雑なシステムや複数のクラス群に対して、統一的で簡潔な窓口を提供するデザインパターンです。 クライアントは内部の詳細を意識せず、ファサードを通じて必要な処理を呼び出せます。
その反面でファサードに機能を集めすぎると責務が肥大化してしまい、結局複雑な設計になってしまう可能性もあるため、使い方には注意が必要です。
ファサードパターンの構成と用語
ファサードパターンは以下のようなクラスで構成されています。
-
Facade(ファサード)
- 複雑な
サブシステムに対して、統一的で簡潔な窓口を提供するクラス -
クライアントはこの窓口を通じて操作をする
- 複雑な
-
Subsystem Classes(サブシステム群)
- 実際の処理を担当するクラス群
-
ファサードから呼び出されるが、それぞれ独立した責務を持つ
-
Client(クライアント)
-
ファサードを利用する側 - 内部の詳細を知らずに、
ファサードのインターフェースだけを使う
-
構造の図解
コード例
ラーメンの調理を例にしたファサードパターンのサンプルコード
このコードは小規模なのでファイル分けなどを行っていませんがご容赦ください。
//---------------------------------------------------------------
//! @file main.cpp
//! @brief ラーメンの調理を例にしたファサードパターンのサンプルコード
//! @author つきの
//---------------------------------------------------------------
#include <iostream>
#include <string>
//---------------------------------------------------------------
//! @class NoodleBoiler
//! @brief 麺を茹でるサブシステム
//! @note ファサードパターンのSubSystem(サブシステム)に該当する
//---------------------------------------------------------------
class NoodleBoiler {
public:
//---------------------------------------------------------------
//! @brief 麺を茹でる関数
//---------------------------------------------------------------
void boil() { std::cout << "麺を茹でました\n"; }
};
//---------------------------------------------------------------
//! @class SoupPreparer
//! @brief スープを準備するサブシステム
//! @note ファサードパターンのSubSystem(サブシステム)に該当する
//---------------------------------------------------------------
class SoupPreparer {
public:
//---------------------------------------------------------------
//! @brief スープを準備する関数
//! @param type スープの種類
//---------------------------------------------------------------
void prepare(const std::string& type) { std::cout << type << "スープを準備しました\n"; }
};
//---------------------------------------------------------------
//! @class ToppingAdder
//! @brief トッピングを追加するサブシステム
//! @note ファサードパターンのSubSystem(サブシステム)に該当する
//---------------------------------------------------------------
class ToppingAdder {
public:
//---------------------------------------------------------------
//! @brief トッピングを追加する関数
//! @param topping トッピングの名前
//---------------------------------------------------------------
void add(const std::string& topping) { std::cout << topping << "をトッピングしました\n"; }
};
//---------------------------------------------------------------
//! @class RamenOrderFacade
//! @brief ラーメン注文のファサード
//! @note ファサードパターンのFacade(ファサード)に該当する
//---------------------------------------------------------------
class RamenOrderFacade {
private:
NoodleBoiler noodle_boiler; // 麺を茹でるサブシステム
SoupPreparer soup_preparer; // スープを準備するサブシステム
ToppingAdder topping_adder; // トッピングを追加するサブシステム
public:
//---------------------------------------------------------------
//! @brief ラーメンを注文する関数
//! @param soup_type スープの種類
//! @param topping_name トッピングの名前
//---------------------------------------------------------------
void orderRamen(const std::string& soup_type, const std::string& topping_name) {
noodle_boiler.boil(); // 麺を茹でる
soup_preparer.prepare(soup_type); // スープを準備する
topping_adder.add(topping_name); // トッピングを追加する
std::cout << "ラーメン完成!\n"; // ラーメン完成のメッセージ
}
};
//---------------------------------------------------------------
//! @brief メイン関数、エントリポイント
//! @return 終了コード
//! @note ファサードパターンの(クライアント)に該当する
//---------------------------------------------------------------
int main() {
// ラーメン注文のファサード(店)を作成
RamenOrderFacade ramen_shop;
// ラーメンを注文(豚骨醤油ベースのスープにニンニクヤサイをマシマシ)
ramen_shop.orderRamen("とんこつ醤油", "ニンニクヤサイマシマシ");
return 0;
}
麺を茹でました
とんこつ醤油スープを準備しました
ニンニクヤサイマシマシをトッピングしました
ラーメン完成!
ラーメンが完成しました。
クラス図
Facade/ファサード
本記事では麺茹で スープ入れ トッピングの調理工程をファサードに閉じ込めることで、クライアントが注文を行うだけでラーメンを完成させることが出来ます。
//---------------------------------------------------------------
//! @class RamenOrderFacade
//! @brief ラーメン注文のファサード
//! @note ファサードパターンのFacade(ファサード)に該当する
//---------------------------------------------------------------
class RamenOrderFacade {
private:
NoodleBoiler noodle_boiler; // 麺を茹でるサブシステム
SoupPreparer soup_preparer; // スープを準備するサブシステム
ToppingAdder topping_adder; // トッピングを追加するサブシステム
public:
//---------------------------------------------------------------
//! @brief ラーメンを注文する関数
//! @param soup_type スープの種類
//! @param topping_name トッピングの名前
//---------------------------------------------------------------
void orderRamen(const std::string& soup_type, const std::string& topping_name) {
noodle_boiler.boil(); // 麺を茹でる
soup_preparer.prepare(soup_type); // スープを準備する
topping_adder.add(topping_name); // トッピングを追加する
std::cout << "ラーメン完成!\n"; // ラーメン完成のメッセージ
}
};
総括
-
ファサードパターンを使用することで、複雑な処理を隠してクライアントが操作を行いやすくすることが可能となる - 乱用は責務の肥大化を招くため推奨されない