ファクトリ & アブストラクトファクトリ
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 92/104。
Factoryパターンは、オブジェクトの生成を別のメソッドやクラスに委譲することで、具体的なクラスを指定せずにオブジェクトを生成できるようにします。これは、生成ロジックが複雑な場合や、クライアントコードを具体的な実装から切り離したい場合に役立ちます。
シンプルなファクトリは、入力に基づいて異なる派生型を返すメソッドを使用します:
#include <iostream>
#include <memory>
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// ファクトリ関数
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}Abstract Factory は、関連するオブジェクトのファミリーを作成することで、これをさらに進展させます。1つのファクトリメソッドの代わりに、複数の作成メソッドを持つファクトリインターフェースを使用します:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// Abstract Factory
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};柔軟なオブジェクト生成が必要な場合は Factory を、関連するオブジェクトが整合性を持って連携することを保証する必要がある場合は Abstract Factory を使用してください。
チャレンジ
簡単FactoryパターンとAbstract Factoryパターンを使用して、ドキュメント生成(Document Generator)システムを構築しましょう。異なる出力形式(PDFとHTML)に対して、異なる種類のドキュメント(レポートと請求書)を生成できるシステムを作成し、ファクトリがどのようにオブジェクトの作成をクライアントコードから分離するかを学びます。
コードは以下の4つのファイルに分けて構成します:
Document.h: ドキュメント製品の階層を定義します。生成されたドキュメントを説明する文字列を返す純粋仮想関数
generate()と、仮想デストラクタを持つ抽象基底クラスDocumentを作成します。次に、4つの具体的なドキュメントクラスを作成します:
PDFReport—generate()は"Generating PDF Report"を返しますHTMLReport—generate()は"Generating HTML Report"を返しますPDFInvoice—generate()は"Generating PDF Invoice"を返しますHTMLInvoice—generate()は"Generating HTML Invoice"を返します
DocumentFactory.h: シンプルなファクトリ関数と抽象ファクトリの両方を実装します。まず、2つの文字列(ドキュメントタイプ
"report"または"invoice"と、形式"pdf"または"html")を受け取るcreateDocumentというシンプルなファクトリ関数を作成します。これは、適切な具体的なドキュメントを指すstd::unique_ptr<Document>を返す必要があります。無効な組み合わせの場合はnullptrを返します。次に、2つの純粋仮想メソッドを持つ
DocumentFactoryという抽象ファクトリクラスを作成します:createReport()—std::unique_ptr<Document>を返しますcreateInvoice()—std::unique_ptr<Document>を返します
2つの具体的なファクトリを実装します:
PDFFactory—PDFReportとPDFInvoiceを作成しますHTMLFactory—HTMLReportとHTMLInvoiceを作成します
DocumentService.h: 抽象ファクトリを使用するサービスを作成します。コンストラクタで
std::unique_ptr<DocumentFactory>を受け取るDocumentServiceクラスを定義します。このクラスにはgenerateAllDocuments()というメソッドを持たせ、ファクトリを使用してレポートと請求書の両方を作成し、それぞれのgenerate()を呼び出した結果を1行に1つずつ出力します。main.cpp: 両方のファクトリアプローチを実演します。2つの入力を読み取ります:
- ドキュメントタイプ(
reportまたはinvoice) - 形式(
pdfまたはhtml)
まず、シンプルなファクトリ関数を使用して、入力に基づいたドキュメントを作成します。その
generate()を呼び出した結果を出力します。次に、空行を出力します。
続いて、形式の入力に基づいて適切なファクトリ(
pdfの場合はPDFFactory、htmlの場合はHTMLFactory)を使用してDocumentServiceを作成します。generateAllDocuments()を呼び出して、抽象ファクトリがどのように関連するドキュメントファミリーを作成するかを示します。- ドキュメントタイプ(
例えば、入力が report と pdf の場合:
Generating PDF Report
Generating PDF Report
Generating PDF Invoice入力が invoice と html の場合:
Generating HTML Invoice
Generating HTML Report
Generating HTML Invoiceシンプルなファクトリが両方のパラメータに基づいて単一のドキュメントを作成するのに対し、抽象ファクトリはサービスによって作成されるすべてのドキュメントが同じ形式を共有することを保証している点に注目してください。これは、一貫して連携する必要がある関連オブジェクトのファミリーが必要な場合の Abstract Factory の主な利点です。
チートシート
Factoryパターンは、オブジェクトの生成を別のメソッドやクラスに委譲し、具体的なクラスを指定せずにオブジェクトを生成できるようにします。これにより、クライアントコードを具体的な実装から切り離す(デカップリングする)ことができます。
シンプルファクトリは、入力に基づいて異なる派生型を返す関数を使用します:
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// ファクトリ関数
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}Abstract Factoryパターンは、関連するオブジェクトのファミリーを作成します。1つのファクトリメソッドの代わりに、複数の生成メソッドを持つファクトリインターフェースを使用します:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// 抽象ファクトリ
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};柔軟なオブジェクト生成が必要な場合はFactoryを使用します。関連するオブジェクトがファミリーとして一貫して連携することを保証する必要がある場合は、Abstract Factoryを使用します。
自分で試してみよう
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// 入力を読み込む
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: シンプルファクトリ関数を使用してドキュメントを作成する
// documentType と format の入力に基づく
// それに対して generate() を呼び出した結果を出力する
// 空行を出力する
cout << endl;
// TODO: 適切なファクトリを使用して DocumentService を作成する
// format の入力に基づく ("pdf" の場合は PDFFactory、"html" の場合は HTMLFactory)
// generateAllDocuments() を呼び出して抽象ファクトリを実演する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス