Post

팩토리(Factory) 패턴

클래스 기반 팩토리 패턴 이해

팩토리(Factory) 패턴

팩토리 패턴은 객체 생성을 담당하는 클래스를 분리하여 코드의 유지소수성과 확장성을 높이는 디자인 패턴이다.

즉, 객체 생성 로직을 캡슐화하고 객체 생성을 클라이언트 코드에서 분리할 때 사용한다.

주요 개념

  • 팩토리(Factory)
    • 객체 생성을 담당하며 클라이언트가 직접 객체를 생성하지 않도록 함
    • 상속을 활용하여 다양한 하위 클래스 생성 가능
graph TD
    A[Client] -->|Request| B[Factory]
    B -->|Create and return| C[Product A]
    B -->|Create and return| D[Product B]
    
    subgraph "Structure of Factory"
        B
        C
        D
    end

장점

  • 객체 생성 로직을 클라이언트에서 분리 가능 → 유지보수성 향상
    • 새로운 제품을 추가할 때 팩토리 클래스만 수정하면 됨
    • 의존성을 줄이고, 코드의 유연성을 높임

주의점

  • 제품군(Product)과 팩토리의 관계를 명확한 정의 필요
    • 팩토리는 반환할 객체의 인터페이스(또는 추상 클래스)를 기반으로 동작해야 함
    • 구체적인 클래스에 의존하지 않도록 스마트 포인터를 사용을 추천
  • 팩토리의 확장성 고려
    • 새로운 제품을 추가할 때 Factory의 if-else 문이 길어질 수 있음
    • 이러한 경우에는 등록 기반 팩토리(Factory Map) 또는 추상 팩토리(Factory Method 패턴)를 고려
  • 메모리 누수, 오버헤드 문제 주의
    • 팩토리 패턴은 객체를 동적으로 생성하는 경우가 많아 성능 및 메모리 관리 필요
    • std::unique_ptr 또는 std::shared_ptr을 사용하여 메모리 누수를 방지필요
    • 객체 생성 비용이 크다면 싱글톤(Singleton)이나 객체 풀(Object Pool) 패턴과 조합을 고려
  • 의존성 주입(DI)와의 조합 고려
    • 팩토리 패턴만 사용할 경우 객체 생성이 팩토리에 묶일 수 있음
    • 의존성 주입(DI) 프레임워크와 함께 사용하면 더 유연한 설계가 가능

예시

C++을 이용하여 예시 코드를 작성하였다. 아래 링크에서 전체를 확인할 수 있다.

https://github.com/grade-e/factory-cpp-container

Class diagram

classDiagram
    class Sensor {
        +virtual ~Sensor()
        +virtual void read() const = 0
        +int getId() const
    }
    
    class TemperatureSensor {
        +TemperatureSensor(int id)
        +void read() const override
    }
    
    class PressureSensor {
        +PressureSensor(int id)
        +void read() const override
    }

    class SensorModule {
        +virtual ~SensorModule()
        +void readAll() const
        #vector<unique_ptr<Sensor>> sensors_
        #static int counter_
    }

    class TemperatureSensorModule {
        +TemperatureSensorModule(int sensorCount)
    }

    class PressureSensorModule {
        +PressureSensorModule(int sensorCount)
    }

    class SensorFactory {
        +static unique_ptr<SensorModule> createTemperatureModule(int sensorCount)
        +static unique_ptr<SensorModule> createPressureModule(int sensorCount)
    }

    Sensor <|-- TemperatureSensor
    Sensor <|-- PressureSensor
    SensorModule <|-- TemperatureSensorModule
    SensorModule <|-- PressureSensorModule
    SensorModule "1" *-- "many" Sensor : contains
    SensorFactory --> TemperatureSensorModule
    SensorFactory --> PressureSensorModule

코드

Sensor

  • 추상 클래스이므로, 직접 객체로 생성되지 않음
  • 센서 객체는 read() 메서드를 가지며, ID를 고유한 정수값으로 부여.
1
2
3
4
5
6
7
8
9
10
11
12
class Sensor {
   public:
    virtual ~Sensor() = default;
    virtual void read() const = 0;
    int getId() const { return id_; }

   protected:
    explicit Sensor(int id) : id_(id) {}

   private:
    int id_;
};

Range sensor, Temperature sensor

  • Sensor를 상속받아 거리센서, 온도센서 처리
  • 생성될 때 id를 부여받음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class RangeSensor : public Sensor {
  public:
    explicit RangeSensor(int id) : Sensor(id) {}

    void read() const override {
      std::cout << "Reading data from range sensor " << getId() << std::endl;
    }
};

class TemperatureSensor : public Sensor {
    public:
    TemperatureSensor(int id) : Sensor(id) {}

    void read() const override {
        std::cout << "Reading data from temperature sensor " << getId()
                  << std::endl;
    }
};

SensorModule

  • 추상 클래스이므로, 직접 객체로 생성되지 않음
  • 각 센서를 관리 및 데이터 처리하는 역할을 수행
  • 센서를 여러 개 포함하는 집합적 개념
    • 센서 객체들의 컨테이너std::vector<unique_ptr<Sensor>>를 포함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SensorModule {
  public:
    virtual ~SensorModule() = default;

    virtual void processData() const = 0;  // 센서 모듈별 데이터 처리 방식

    void readAll() const {
        for (const auto& sensor : sensors_) {
          sensor->read();
        }
    }

   protected:
    std::vector<std::unique_ptr<Sensor>> sensors_;
    static int counter_;
};

Range sensor module, Temperature sensor module

  • SensorModule을 상속받아, 각각 온도 센서와 압력 센서를 관리하는 모듈
  • 센서의 개수를 인자로 받아, 생성자에서 자동으로 센서를 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class RangeSensorModule : public SensorModule {
   public:
    explicit RangeSensorModule(int count) {
        counter_ = 0;
        for (int i = 0; i < count; ++i) {
            sensors_.push_back(std::make_unique<RangeSensor>(++counter_));
        }
    }

    void processData() const override {
        std::cout << "[Range Module] Processing range data..." << std::endl;
    }

   private:
    int counter_;
};

class TemperatureSensorModule : public SensorModule {
   public:
    explicit TemperatureSensorModule(int count) {
        counter_ = 0;
        for (int i = 0; i < count; ++i) {
            sensors_.push_back(std::make_unique<TemperatureSensor>(++counter_));
        }
    }

    void processData() const override {
        std::cout << "[Temperature Module] Processing temperature data..."
                  << std::endl;
    }

   private:
    int counter_;
};

Sensor Factory

  • SensorModule을 직접 생성하는 것이 아니라, Factory를 통해 객체 생성을 캡슐화
1
2
3
4
5
6
7
8
9
10
class SensorFactory {
  public:
    static std::unique_ptr<SensorModule> createTemperatureModule(int count) {
        return std::make_unique<TemperatureSensorModule>(count);
    }

    static std::unique_ptr<SensorModule> createRangeModule(int count) {
      return std::make_unique<RangeSensorModule>(count);
    }
};

main()

  • Range sensor, Temparature sensor의 생성 갯수를 Factory에서 지정
  • Factory에서 각 SensorModule 생성 이후, 제어는 SensorModule에서 진행
1
2
3
4
5
6
7
8
9
10
11
12
int main() {
    auto tempModule = SensorFactory::createTemperatureModule(2);
    auto rangeModule = SensorFactory::createRangeModule(3);

    tempModule->readAll();
    rangeModule->readAll();

    tempModule->processData();
    rangeModule->processData();

    return 0;
}

실행 결과

1
2
3
4
5
6
7
Reading data from temperature sensor 1
Reading data from temperature sensor 2
Reading data from range sensor 1
Reading data from range sensor 2
Reading data from range sensor 3
[Temperature Module] Processing temperature data...
[Range Module] Processing range data...
This post is licensed under CC BY 4.0 by the author.