Menu
Coddy logo textTech

팩토리 & 추상 팩토리

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 92번째.

팩토리 패턴(Factory pattern)은 객체 생성을 별도의 메서드나 클래스에 위임하여, 정확한 클래스를 지정하지 않고도 객체를 생성할 수 있게 해줍니다. 이는 생성 로직이 복잡하거나 클라이언트 코드를 구체적인 구현체로부터 분리하고 싶을 때 유용합니다.

심플 팩토리(Simple 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)는 연관된 객체들의 제품군을 생성함으로써 이를 한 단계 더 발전시킵니다. 하나의 팩토리 메서드 대신, 여러 개의 생성 메서드를 가진 팩토리 인터페이스를 갖게 됩니다:

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

챌린지

쉬움

팩토리(Factory) 및 추상 팩토리(Abstract Factory) 패턴을 사용하여 문서 생성기(Document Generator) 시스템을 구축해 보겠습니다. 서로 다른 출력 형식(PDF 및 HTML)에 대해 다양한 유형의 문서(보고서 및 송장)를 생성할 수 있는 시스템을 만들어, 팩토리가 어떻게 클라이언트 코드에서 객체 생성을 분리하는지 실습합니다.

코드는 다음 네 개의 파일로 구성됩니다:

  • Document.h: 문서 제품 계층 구조를 정의합니다.

    생성된 문서를 설명하는 문자열을 반환하는 순수 가상 메서드 generate()와 가상 소멸자를 가진 추상 기본 클래스 Document를 생성하세요.

    그런 다음 네 개의 구체적인 문서 클래스를 생성하세요:

    • PDFReportgenerate()"Generating PDF Report"를 반환합니다.
    • HTMLReportgenerate()"Generating HTML Report"를 반환합니다.
    • PDFInvoicegenerate()"Generating PDF Invoice"를 반환합니다.
    • HTMLInvoicegenerate()"Generating HTML Invoice"를 반환합니다.
  • DocumentFactory.h: 단순 팩토리 함수와 추상 팩토리를 모두 구현합니다.

    먼저, 문서 유형("report" 또는 "invoice")과 형식("pdf" 또는 "html")이라는 두 개의 문자열을 매개변수로 받는 createDocument라는 단순 팩토리 함수를 만드세요. 이 함수는 적절한 구체적 문서를 가리키는 std::unique_ptr<Document>를 반환해야 합니다. 잘못된 조합에 대해서는 nullptr를 반환하세요.

    그런 다음 두 개의 순수 가상 메서드를 가진 DocumentFactory라는 추상 팩토리 클래스를 만드세요:

    • createReport()std::unique_ptr<Document>를 반환합니다.
    • createInvoice()std::unique_ptr<Document>를 반환합니다.

    두 개의 구체적인 팩토리를 구현하세요:

    • PDFFactoryPDFReportPDFInvoice를 생성합니다.
    • HTMLFactoryHTMLReportHTMLInvoice를 생성합니다.
  • DocumentService.h: 추상 팩토리를 사용하는 서비스를 생성합니다.

    생성자에서 std::unique_ptr<DocumentFactory>를 받는 DocumentService 클래스를 정의하세요. 이 클래스는 팩토리를 사용하여 보고서와 송장을 모두 생성한 다음, 각각에 대해 generate()를 호출한 결과를 한 줄에 하나씩 출력하는 generateAllDocuments() 메서드를 가져야 합니다.

  • main.cpp: 두 가지 팩토리 접근 방식을 시연합니다.

    두 개의 입력을 읽습니다:

    1. 문서 유형 (report 또는 invoice)
    2. 형식 (pdf 또는 html)

    먼저, 입력을 기반으로 문서를 생성하기 위해 단순 팩토리 함수를 사용하세요. 해당 문서의 generate() 호출 결과를 출력합니다.

    그런 다음 빈 줄을 하나 출력합니다.

    다음으로, 형식 입력에 따라 적절한 팩토리(pdf의 경우 PDFFactory, html의 경우 HTMLFactory)를 사용하여 DocumentService를 생성하세요. generateAllDocuments()를 호출하여 추상 팩토리가 어떻게 관련된 문서 제품군을 생성하는지 보여줍니다.

예를 들어, 입력이 reportpdf인 경우:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

입력이 invoicehtml인 경우:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

단순 팩토리는 두 매개변수를 모두 기반으로 단일 문서를 생성하는 반면, 추상 팩토리는 서비스에서 생성된 모든 문서가 동일한 형식을 공유하도록 보장한다는 점에 주목하세요. 이것이 바로 일관되게 함께 작동해야 하는 관련 객체 제품군이 필요할 때 추상 팩토리가 제공하는 핵심 이점입니다.

치트 시트

팩토리 패턴(Factory pattern)은 객체 생성을 별도의 메서드나 클래스에 위임하여, 정확한 클래스를 지정하지 않고도 객체를 생성할 수 있게 해줍니다. 이는 클라이언트 코드를 구체적인 구현체로부터 분리합니다.

심플 팩토리(simple 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"; }
};

// Factory function
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 pattern)은 관련 객체들의 패밀리를 생성합니다. 하나의 팩토리 메서드 대신, 여러 생성 메서드가 포함된 팩토리 인터페이스를 가집니다:

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)를 사용하세요.

직접 해보기

#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: simple factory 함수를 사용하여 문서를 생성하세요
    // documentType 및 format 입력을 기반으로
    // 해당 객체에서 generate()를 호출한 결과를 출력하세요
    
    // 빈 줄 출력
    cout << endl;
    
    // TODO: 적절한 팩토리를 사용하여 DocumentService를 생성하세요
    // format 입력에 따라 ("pdf"의 경우 PDFFactory, "html"의 경우 HTMLFactory)
    // 추상 팩토리를 시연하기 위해 generateAllDocuments()를 호출하세요
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨