팩토리 & 추상 팩토리
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를 사용하세요.
챌린지
쉬움팩토리(Factory) 및 추상 팩토리(Abstract Factory) 패턴을 사용하여 문서 생성기(Document Generator) 시스템을 구축해 보겠습니다. 서로 다른 출력 형식(PDF 및 HTML)에 대해 다양한 유형의 문서(보고서 및 송장)를 생성할 수 있는 시스템을 만들어, 팩토리가 어떻게 클라이언트 코드에서 객체 생성을 분리하는지 실습합니다.
코드는 다음 네 개의 파일로 구성됩니다:
Document.h: 문서 제품 계층 구조를 정의합니다.생성된 문서를 설명하는 문자열을 반환하는 순수 가상 메서드
generate()와 가상 소멸자를 가진 추상 기본 클래스Document를 생성하세요.그런 다음 네 개의 구체적인 문서 클래스를 생성하세요:
PDFReport—generate()는"Generating PDF Report"를 반환합니다.HTMLReport—generate()는"Generating HTML Report"를 반환합니다.PDFInvoice—generate()는"Generating PDF Invoice"를 반환합니다.HTMLInvoice—generate()는"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>를 반환합니다.
두 개의 구체적인 팩토리를 구현하세요:
PDFFactory—PDFReport및PDFInvoice를 생성합니다.HTMLFactory—HTMLReport및HTMLInvoice를 생성합니다.
DocumentService.h: 추상 팩토리를 사용하는 서비스를 생성합니다.생성자에서
std::unique_ptr<DocumentFactory>를 받는DocumentService클래스를 정의하세요. 이 클래스는 팩토리를 사용하여 보고서와 송장을 모두 생성한 다음, 각각에 대해generate()를 호출한 결과를 한 줄에 하나씩 출력하는generateAllDocuments()메서드를 가져야 합니다.main.cpp: 두 가지 팩토리 접근 방식을 시연합니다.두 개의 입력을 읽습니다:
- 문서 유형 (
report또는invoice) - 형식 (
pdf또는html)
먼저, 입력을 기반으로 문서를 생성하기 위해 단순 팩토리 함수를 사용하세요. 해당 문서의
generate()호출 결과를 출력합니다.그런 다음 빈 줄을 하나 출력합니다.
다음으로, 형식 입력에 따라 적절한 팩토리(
pdf의 경우PDFFactory,html의 경우HTMLFactory)를 사용하여DocumentService를 생성하세요.generateAllDocuments()를 호출하여 추상 팩토리가 어떻게 관련된 문서 제품군을 생성하는지 보여줍니다.- 문서 유형 (
예를 들어, 입력이 report와 pdf인 경우:
Generating PDF Report
Generating PDF Report
Generating PDF Invoice입력이 invoice와 html인 경우:
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;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.