본문 바로가기
-----ETC-----/C++ 고급 프로그래밍과 응용 프로젝트 시리즈

[C++ 고급 프로그래밍과 응용 프로젝트 시리즈] Day 9: 디자인 패턴 심화 - 팩토리 패턴 (Factory Pattern)

by cogito21_cpp 2024. 8. 1.
반응형

팩토리 패턴 (Factory Pattern)

팩토리 패턴은 객체 생성 로직을 별도의 팩토리 클래스로 분리하여, 객체 생성의 유연성과 확장성을 높이는 디자인 패턴입니다. 이는 객체 생성 방식을 캡슐화하여 클라이언트 코드가 객체 생성 방법에 의존하지 않도록 합니다.

 

팩토리 패턴의 종류

  1. 단순 팩토리 패턴 (Simple Factory Pattern)
  2. 팩토리 메서드 패턴 (Factory Method Pattern)
  3. 추상 팩토리 패턴 (Abstract Factory Pattern)

단순 팩토리 패턴 (Simple Factory Pattern)

단순 팩토리 패턴은 객체 생성을 위한 메서드를 제공하는 클래스를 정의합니다. 이 클래스는 클라이언트 코드 대신 객체를 생성합니다.

 

단순 팩토리 패턴 예제

#include <iostream>
#include <memory>

class Product {
public:
    virtual void use() = 0;
    virtual ~Product() = default;
};

class ConcreteProductA : public Product {
public:
    void use() override {
        std::cout << "Using Product A" << std::endl;
    }
};

class ConcreteProductB : public Product {
public:
    void use() override {
        std::cout << "Using Product B" << std::endl;
    }
};

class SimpleFactory {
public:
    std::unique_ptr<Product> createProduct(char type) {
        if (type == 'A') {
            return std::make_unique<ConcreteProductA>();
        } else if (type == 'B') {
            return std::make_unique<ConcreteProductB>();
        }
        return nullptr;
    }
};

int main() {
    SimpleFactory factory;
    auto productA = factory.createProduct('A');
    auto productB = factory.createProduct('B');

    if (productA) {
        productA->use();
    }

    if (productB) {
        productB->use();
    }

    return 0;
}

 

이 예제에서 SimpleFactory 클래스는 createProduct 메서드를 통해 Product 객체를 생성합니다. Product 객체의 구체적인 타입은 char 타입의 인자에 따라 결정됩니다.

 


팩토리 메서드 패턴 (Factory Method Pattern)

팩토리 메서드 패턴은 객체 생성을 위한 인터페이스를 정의하고, 실제 객체 생성은 서브클래스에서 수행하도록 합니다. 이를 통해 객체 생성의 책임을 서브클래스에 위임합니다.

 

팩토리 메서드 패턴 예제

#include <iostream>
#include <memory>

class Product {
public:
    virtual void use() = 0;
    virtual ~Product() = default;
};

class ConcreteProductA : public Product {
public:
    void use() override {
        std::cout << "Using Product A" << std::endl;
    }
};

class ConcreteProductB : public Product {
public:
    void use() override {
        std::cout << "Using Product B" << std::endl;
    }
};

class Creator {
public:
    virtual std::unique_ptr<Product> factoryMethod() = 0;

    void someOperation() {
        auto product = factoryMethod();
        if (product) {
            product->use();
        }
    }

    virtual ~Creator() = default;
};

class ConcreteCreatorA : public Creator {
public:
    std::unique_ptr<Product> factoryMethod() override {
        return std::make_unique<ConcreteProductA>();
    }
};

class ConcreteCreatorB : public Creator {
public:
    std::unique_ptr<Product> factoryMethod() override {
        return std::make_unique<ConcreteProductB>();
    }
};

int main() {
    std::unique_ptr<Creator> creatorA = std::make_unique<ConcreteCreatorA>();
    creatorA->someOperation();

    std::unique_ptr<Creator> creatorB = std::make_unique<ConcreteCreatorB>();
    creatorB->someOperation();

    return 0;
}

 

이 예제에서 Creator 클래스는 factoryMethod 인터페이스를 정의하고, 서브클래스인 ConcreteCreatorAConcreteCreatorB는 실제 객체 생성 로직을 구현합니다. 클라이언트 코드는 someOperation 메서드를 통해 객체를 생성하고 사용합니다.


추상 팩토리 패턴 (Abstract Factory Pattern)

추상 팩토리 패턴은 관련 객체들을 생성하기 위한 인터페이스를 제공하며, 실제 객체 생성은 구체적인 팩토리 클래스에서 수행합니다. 이를 통해 제품군을 생성할 수 있습니다.

 

추상 팩토리 패턴 예제

#include <iostream>
#include <memory>

// 추상 제품 A
class AbstractProductA {
public:
    virtual void use() = 0;
    virtual ~AbstractProductA() = default;
};

// 구체적인 제품 A1
class ConcreteProductA1 : public AbstractProductA {
public:
    void use() override {
        std::cout << "Using Product A1" << std::endl;
    }
};

// 구체적인 제품 A2
class ConcreteProductA2 : public AbstractProductA {
public:
    void use() override {
        std::cout << "Using Product A2" << std::endl;
    }
};

// 추상 제품 B
class AbstractProductB {
public:
    virtual void use() = 0;
    virtual ~AbstractProductB() = default;
};

// 구체적인 제품 B1
class ConcreteProductB1 : public AbstractProductB {
public:
    void use() override {
        std::cout << "Using Product B1" << std::endl;
    }
};

// 구체적인 제품 B2
class ConcreteProductB2 : public AbstractProductB {
public:
    void use() override {
        std::cout << "Using Product B2" << std::endl;
    }
};

// 추상 팩토리
class AbstractFactory {
public:
    virtual std::unique_ptr<AbstractProductA> createProductA() = 0;
    virtual std::unique_ptr<AbstractProductB> createProductB() = 0;
    virtual ~AbstractFactory() = default;
};

// 구체적인 팩토리 1
class ConcreteFactory1 : public AbstractFactory {
public:
    std::unique_ptr<AbstractProductA> createProductA() override {
        return std::make_unique<ConcreteProductA1>();
    }
    std::unique_ptr<AbstractProductB> createProductB() override {
        return std::make_unique<ConcreteProductB1>();
    }
};

// 구체적인 팩토리 2
class ConcreteFactory2 : public AbstractFactory {
public:
    std::unique_ptr<AbstractProductA> createProductA() override {
        return std::make_unique<ConcreteProductA2>();
    }
    std::unique_ptr<AbstractProductB> createProductB() override {
        return std::make_unique<ConcreteProductB2>();
    }
};

int main() {
    std::unique_ptr<AbstractFactory> factory1 = std::make_unique<ConcreteFactory1>();
    auto productA1 = factory1->createProductA();
    auto productB1 = factory1->createProductB();
    productA1->use();
    productB1->use();

    std::unique_ptr<AbstractFactory> factory2 = std::make_unique<ConcreteFactory2>();
    auto productA2 = factory2->createProductA();
    auto productB2 = factory2->createProductB();
    productA2->use();
    productB2->use();

    return 0;
}

 

이 예제에서 AbstractFactory 클래스는 제품 A와 B를 생성하는 인터페이스를 정의합니다. ConcreteFactory1ConcreteFactory2는 구체적인 제품 A1, A2와 B1, B2를 생성합니다. 클라이언트 코드는 팩토리 객체를 통해 제품을 생성하고 사용합니다.


실습 문제

문제 1: 단순 팩토리 패턴을 사용하여 도형 클래스 구현

단순 팩토리 패턴을 사용하여 원, 사각형, 삼각형을 생성하는 도형 클래스를 구현하세요.

 

해설:

#include <iostream>
#include <memory>

class Shape {
public:
    virtual void draw() = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Circle" << std::endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Square" << std::endl;
    }
};

class Triangle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Triangle" << std::endl;
    }
};

class ShapeFactory {
public:
    std::unique_ptr<Shape> createShape(const std::string& type) {
        if (type == "circle") {
            return std::make_unique<Circle>();
        } else if (type == "square") {
            return std::make_unique<Square>();
        } else if (type == "triangle") {
            return std::make_unique<Triangle>();
        }
        return nullptr;
    }
};

int main() {


    ShapeFactory factory;
    auto circle = factory.createShape("circle");
    auto square = factory.createShape("square");
    auto triangle = factory.createShape("triangle");

    if (circle) {
        circle->draw();
    }
    if (square) {
        square->draw();
    }
    if (triangle) {
        triangle->draw();
    }

    return 0;
}

 

문제 2: 팩토리 메서드 패턴을 사용하여 파일 로더 클래스 구현

팩토리 메서드 패턴을 사용하여 텍스트 파일과 바이너리 파일을 로드하는 파일 로더 클래스를 구현하세요.

 

해설:

#include <iostream>
#include <memory>

class FileLoader {
public:
    virtual void load() = 0;
    virtual ~FileLoader() = default;
};

class TextFileLoader : public FileLoader {
public:
    void load() override {
        std::cout << "Loading Text File" << std::endl;
    }
};

class BinaryFileLoader : public FileLoader {
public:
    void load() override {
        std::cout << "Loading Binary File" << std::endl;
    }
};

class LoaderCreator {
public:
    virtual std::unique_ptr<FileLoader> factoryMethod() = 0;

    void loadFile() {
        auto loader = factoryMethod();
        if (loader) {
            loader->load();
        }
    }

    virtual ~LoaderCreator() = default;
};

class TextLoaderCreator : public LoaderCreator {
public:
    std::unique_ptr<FileLoader> factoryMethod() override {
        return std::make_unique<TextFileLoader>();
    }
};

class BinaryLoaderCreator : public LoaderCreator {
public:
    std::unique_ptr<FileLoader> factoryMethod() override {
        return std::make_unique<BinaryFileLoader>();
    }
};

int main() {
    std::unique_ptr<LoaderCreator> textLoader = std::make_unique<TextLoaderCreator>();
    textLoader->loadFile();

    std::unique_ptr<LoaderCreator> binaryLoader = std::make_unique<BinaryLoaderCreator>();
    binaryLoader->loadFile();

    return 0;
}

 

이제 9일차의 학습을 마쳤습니다. 팩토리 패턴의 다양한 구현 방법과 활용 사례에 대해 상세히 학습하고 실습 문제를 풀어보았습니다.

 

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "전략 패턴"에 대해 학습하겠습니다.

반응형