Menu
CoddyTech

팩토리 & 추상 팩토리

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

Factory 패턴은 객체 생성을 별도의 메서드나 class에 위임하여, 정확한 class를 지정하지 않고도 객체를 create할 수 있게 합니다. 이는 생성 로직이 복잡하거나 클라이언트 코드를 concrete 구현과 분리하려는 경우에 유용합니다.

간단한 팩토리는 입력에 따라 서로 다른 파생 타입을 반환하는 메서드를 사용합니다:

#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

챌린지

쉬움

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

코드를 네 개의 파일로 구성합니다.

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

    순수 가상 메서드 generate()를 포함하는 추상 기본 class Document를 만듭니다. 이 메서드는 생성된 문서를 설명하는 문자열을 반환하며, 가상 소멸자도 포함합니다.

    그런 다음 네 개의 concrete 문서 class를 만듭니다.

    • 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를 모두 구현합니다.

    먼저 createDocument라는 simple factory function을 만듭니다. 이 함수는 두 개의 문자열을 받습니다. 하나는 문서 유형("report" 또는 "invoice")이고, 다른 하나는 형식("pdf" 또는 "html")입니다. 적절한 concrete 문서를 가리키는 std::unique_ptr<Document>를 반환해야 합니다. 유효하지 않은 조합에는 nullptr를 반환합니다.

    그런 다음 두 개의 순수 가상 메서드를 가진 DocumentFactory라는 abstract factory class를 만듭니다.

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

    두 개의 concrete factory를 구현합니다.

    • PDFFactory: PDFReport와 PDFInvoice를 생성합니다.
    • HTMLFactory: HTMLReport와 HTMLInvoice를 생성합니다.
  • DocumentService.h: abstract factory를 사용하는 서비스를 만듭니다.

    생성자에서 std::unique_ptr<DocumentFactory>를 받는 DocumentService class를 정의합니다. 이 class에는 factory를 사용하여 report와 invoice를 모두 생성한 다음, 각각에 대해 generate()를 호출한 결과를 한 줄에 하나씩 출력하는 generateAllDocuments()라는 메서드가 있어야 합니다.

  • main.cpp: 두 factory 접근 방식을 모두 시연합니다.

    두 개의 입력을 읽습니다.

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

    먼저 simple factory function을 사용하여 입력에 따라 문서를 생성합니다. 해당 문서에서 generate()를 호출한 결과를 출력합니다.

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

    이어서 형식 입력에 따라 적절한 factory를 사용하여 DocumentService를 생성합니다(pdf에는 PDFFactory를, html에는 HTMLFactory를 사용합니다). generateAllDocuments()를 호출하여 abstract factory가 서로 관련된 문서 family를 생성하는 방식을 보여 줍니다.

예를 들어 입력이 report와 pdf인 경우:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

입력이 invoice와 html인 경우:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

simple factory는 두 매개변수를 모두 기준으로 단일 문서를 생성하는 반면, abstract factory는 서비스에서 생성되는 모든 문서가 동일한 형식을 공유하도록 보장한다는 점에 주목하세요. 함께 일관되게 동작하는 관련 객체 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++ 컴파일러