0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C++】処理の窓口はファサードパターン

Posted at

ソフトウェアが大きくなると、利用者は複数のクラスやモジュールを正しい順序で呼び出さなければならず、コードが複雑になりがちです。
ファサードパターンを使用することで複雑さを隠し、窓口ひとつで必要な処理をまとめて呼び出すことができ、呼び出し側のコードがシンプルになったり、内部の保守性が高まるといったメリットがあります。
本記事では簡単なサンプルコードを例に、ファサードパターンについて紹介したいと思います。

前提知識

  • C++の基礎文法を理解している

ファサードパターンとは

ファサードパターン/Facade Pattern は、複雑なシステムや複数のクラス群に対して、統一的で簡潔な窓口を提供するデザインパターンです。 クライアントは内部の詳細を意識せず、ファサードを通じて必要な処理を呼び出せます。
その反面でファサードに機能を集めすぎると責務が肥大化してしまい、結局複雑な設計になってしまう可能性もあるため、使い方には注意が必要です。

ファサードパターンの構成と用語

ファサードパターンは以下のようなクラスで構成されています。

  • Facade(ファサード)
    • 複雑なサブシステムに対して、統一的で簡潔な窓口を提供するクラス
    • クライアントはこの窓口を通じて操作をする
  • Subsystem Classes(サブシステム群)
    • 実際の処理を担当するクラス群
    • ファサードから呼び出されるが、それぞれ独立した責務を持つ
  • Client(クライアント)
    • ファサードを利用する側
    • 内部の詳細を知らずに、ファサードのインターフェースだけを使う

構造の図解

コード例

ラーメンの調理を例にしたファサードパターンのサンプルコード

このコードは小規模なのでファイル分けなどを行っていませんがご容赦ください。

main.cpp
//---------------------------------------------------------------
//! @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;
}
result
麺を茹でました
とんこつ醤油スープを準備しました
ニンニクヤサイマシマシをトッピングしました
ラーメン完成!

ラーメンが完成しました。

クラス図

Facade/ファサード

本記事では麺茹で スープ入れ トッピングの調理工程をファサードに閉じ込めることで、クライアントが注文を行うだけでラーメンを完成させることが出来ます。

main.cpp
//---------------------------------------------------------------
//! @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";     // ラーメン完成のメッセージ
    }
};

総括

  • ファサードパターンを使用することで、複雑な処理を隠してクライアントが操作を行いやすくすることが可能となる
  • 乱用は責務の肥大化を招くため推奨されない
0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?