FactoryとAbstract Factory
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"; }
};
// 抽象ファクトリ
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 を使用します。
チャレンジ
簡単Document Generatorシステムを、FactoryパターンとAbstract Factoryパターンを使用して構築しましょう。異なる出力形式(PDFとHTML)向けに、異なる種類のドキュメント(レポートと請求書)をgenerateできるシステムをcreateし、factoryがオブジェクトのcreateとクライアントコードをどのように分離するかをdemonstrateします。
コードを4つのファイルに整理します。
Document.h:ドキュメントのproduct階層をdefineします。純粋仮想メソッド
generate()を持つabstract base classDocumentをcreateします。このメソッドは、生成されたドキュメントを説明するstringを返し、virtual destructorも持ちます。次に、4つのconcreteドキュメントclassをcreateします。
PDFReport:generate()は"Generating PDF Report"を返しますHTMLReport:generate()は"Generating HTML Report"を返しますPDFInvoice:generate()は"Generating PDF Invoice"を返しますHTMLInvoice:generate()は"Generating HTML Invoice"を返します
DocumentFactory.h:simple factory functionとabstract factoryの両方をimplementします。まず、
createDocumentというsimple factory functionをcreateします。この関数は2つのstringを受け取ります。1つはドキュメントタイプ("report"または"invoice")、もう1つはformat("pdf"または"html")です。適切なconcreteドキュメントを指すstd::unique_ptr<Document>を返します。無効な組み合わせの場合はnullptrを返します。次に、2つの純粋仮想メソッドを持つ
DocumentFactoryというabstract factory classをcreateします。createReport():std::unique_ptr<Document>を返しますcreateInvoice():std::unique_ptr<Document>を返します
2つのconcrete factoryをimplementします。
PDFFactory:PDFReportとPDFInvoiceをcreateしますHTMLFactory:HTMLReportとHTMLInvoiceをcreateします
DocumentService.h:abstract factoryを使用するserviceをcreateします。コンストラクターで
std::unique_ptr<DocumentFactory>を受け取るDocumentServiceclassをdefineします。generateAllDocuments()というメソッドを持ち、factoryを使用してreportとinvoiceの両方をcreateした後、それぞれに対してgenerate()をcallingした結果を1行ずつ出力します。main.cpp:2つのfactoryアプローチをdemonstrateします。2つの入力を読み取ります。
- ドキュメントタイプ(
reportまたはinvoice) - format(
pdfまたはhtml)
まず、入力に基づいてsimple factory functionを使用してドキュメントをcreateします。そのドキュメントに対して
generate()をcallingした結果を出力します。次に、blank lineを出力します。
続いて、format入力に基づくappropriate factoryを使用して
DocumentServiceをcreateします(pdfにはPDFFactory、htmlにはHTMLFactoryを使用します)。generateAllDocuments()をcallingして、abstract factoryが関連するドキュメントのfamilyをどのようにcreateするかを示します。- ドキュメントタイプ(
例えば、入力がreportとpdfの場合は次のようになります。
Generating PDF Report
Generating PDF Report
Generating PDF Invoice入力がinvoiceとhtmlの場合は次のようになります。
Generating HTML Invoice
Generating HTML Report
Generating HTML Invoicesimple factoryは両方のパラメーターに基づいて単一のドキュメントをcreateする一方、abstract factoryは、serviceによってcreateされるすべてのドキュメントが同じformatを共有することを保証します。これは、一貫して連携する関連オブジェクトのfamilyが必要な場合における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クラス自分で練習してみよう: C++オンラインコンパイラ