Menu
Coddy logo textTech

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 を使用します。

challenge icon

チャレンジ

簡単

Document Generatorシステムを、FactoryパターンとAbstract Factoryパターンを使用して構築しましょう。異なる出力形式(PDFとHTML)向けに、異なる種類のドキュメント(レポートと請求書)をgenerateできるシステムをcreateし、factoryがオブジェクトのcreateとクライアントコードをどのように分離するかをdemonstrateします。

コードを4つのファイルに整理します。

  • Document.h:ドキュメントのproduct階層をdefineします。

    純粋仮想メソッドgenerate()を持つabstract base class Documentをcreateします。このメソッドは、生成されたドキュメントを説明するstringを返し、virtual destructorも持ちます。

    次に、4つのconcreteドキュメントclassをcreateします。

    • PDFReportgenerate()"Generating PDF Report"を返します
    • HTMLReportgenerate()"Generating HTML Report"を返します
    • PDFInvoicegenerate()"Generating PDF Invoice"を返します
    • HTMLInvoicegenerate()"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します。

    • PDFFactoryPDFReportPDFInvoiceをcreateします
    • HTMLFactoryHTMLReportHTMLInvoiceをcreateします
  • DocumentService.h:abstract factoryを使用するserviceをcreateします。

    コンストラクターでstd::unique_ptr<DocumentFactory>を受け取るDocumentService classをdefineします。generateAllDocuments()というメソッドを持ち、factoryを使用してreportとinvoiceの両方をcreateした後、それぞれに対してgenerate()をcallingした結果を1行ずつ出力します。

  • main.cpp:2つのfactoryアプローチをdemonstrateします。

    2つの入力を読み取ります。

    1. ドキュメントタイプ(reportまたはinvoice
    2. format(pdfまたはhtml

    まず、入力に基づいてsimple factory functionを使用してドキュメントをcreateします。そのドキュメントに対してgenerate()をcallingした結果を出力します。

    次に、blank lineを出力します。

    続いて、format入力に基づくappropriate factoryを使用してDocumentServiceをcreateします(pdfにはPDFFactoryhtmlにはHTMLFactoryを使用します)。generateAllDocuments()をcallingして、abstract factoryが関連するドキュメントのfamilyをどのようにcreateするかを示します。

例えば、入力がreportpdfの場合は次のようになります。

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

入力がinvoicehtmlの場合は次のようになります。

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

simple 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;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C++オンラインコンパイラ