Menu
Coddy logo textTech

ファクトリ & アブストラクトファクトリ

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 を使用してください。

challenge icon

チャレンジ

簡単

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

コードは以下の4つのファイルに分けて構成します:

  • Document.h: ドキュメント製品の階層を定義します。

    生成されたドキュメントを説明する文字列を返す純粋仮想関数 generate() と、仮想デストラクタを持つ抽象基底クラス Document を作成します。

    次に、4つの具体的なドキュメントクラスを作成します:

    • PDFReportgenerate()"Generating PDF Report" を返します
    • HTMLReportgenerate()"Generating HTML Report" を返します
    • PDFInvoicegenerate()"Generating PDF Invoice" を返します
    • HTMLInvoicegenerate()"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つの具体的なファクトリを実装します:

    • PDFFactoryPDFReportPDFInvoice を作成します
    • HTMLFactoryHTMLReportHTMLInvoice を作成します
  • DocumentService.h: 抽象ファクトリを使用するサービスを作成します。

    コンストラクタで std::unique_ptr<DocumentFactory> を受け取る DocumentService クラスを定義します。このクラスには generateAllDocuments() というメソッドを持たせ、ファクトリを使用してレポートと請求書の両方を作成し、それぞれの generate() を呼び出した結果を1行に1つずつ出力します。

  • main.cpp: 両方のファクトリアプローチを実演します。

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

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

    まず、シンプルなファクトリ関数を使用して、入力に基づいたドキュメントを作成します。その generate() を呼び出した結果を出力します。

    次に、空行を出力します。

    続いて、形式の入力に基づいて適切なファクトリ(pdf の場合は PDFFactoryhtml の場合は HTMLFactory)を使用して DocumentService を作成します。generateAllDocuments() を呼び出して、抽象ファクトリがどのように関連するドキュメントファミリーを作成するかを示します。

例えば、入力が reportpdf の場合:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

入力が invoicehtml の場合:

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

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

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