추상 팩토리 패턴( Abstract Factory Pattern ) 은 객체 생성을 추상화하는 디자인 패턴 중 하나로, 서로 관련된 객체의 집합을 생성하기 위한 인터페이스를 제공한다.
이 패턴은 추상화된 팩토리 클래스를 통해 관련된 객체를 생성하며, 각 객체는 추상 클래스나 인터페이스를 상속받아 구현된다. 이를 통해 클라이언트 코드는 실제 객체의 클래스에 대한 정보를 알 필요 없이 추상화된 인터페이스를 통해 객체를 생성할 수 있는 특징이 있다.
장점
- 유지보수성과 확장성이 높아진다.
- 클라이언트 코드와 객체 생성코드를 분리하기 때문에 유지보수가 쉬워지고 확장할때 편하다.
- 코드 의존성을 낮춘다.
- 코드 자체를 실제로 추가할 일이 크지 않을 수 있기 때문에, 어떠한 항목에 대하여 자세한 코드를 작성하지 않아도 되어 코드 의존성이 크게 낮아지고, 유연한 코드를 작성할 수 있게된다.
- 쉽게 객체생성 코드를 변경할 수 있다.
- 위 항목에 근거하여, 새로운 구체 클래스를 추가하거나 기존 클래스를 변경하지 않고 객체 생성코드 를 변경할 수 있게된다.
단점
- 코드가 복잡해질 수 있다.
- 다양한 경우의 수를 전부 구현해야 하기 때문에 코드가 의미없이 복잡해질 수 있다.
- 인터페이스와 구현클래스의 구현 난이도가 매우높다.
- 위의 경우에 의거하여 모든 경우의 수를 전부 구현해야 하기 때문에 구현난이도가 매우 높다.
#include <iostream>
#include <memory>
// 추상화 클래스 A
class AbstractProductA {
public:
virtual ~AbstractProductA() {} // 소멸자
virtual void use() const = 0; // 사용시 0 으로
};
// 구체화 클래스 A1
class ConcreteProductA1 : public AbstractProductA { // class ConcreateProductA1은 AbstractProductA를 상속받는다. 자식클래스
public:
void use() const override {
std::cout << "Using ConcreteProductA1" << std::endl;
}
};
// 구체화 클래스 A2
class ConcreteProductA2 : public AbstractProductA { // class ConcreateProductA1은 AbstractProductA를 상속받는다. 자식클래스
public:
void use() const override { // 상속받은 use 를 대채한다.
std::cout << "Using ConcreteProductA2" << std::endl;
}
};
// 추상 클래스 B
class AbstractProductB {
public:
virtual ~AbstractProductB() {}
virtual void consume() const = 0;
};
// 구체화 클래스 B1
class ConcreteProductB1 : public AbstractProductB {
public:
void consume() const override {
std::cout << "Consuming ConcreteProductB1" << std::endl;
}
};
// 구체화 클래스 B2
class ConcreteProductB2 : public AbstractProductB {
public:
void consume() const override {
std::cout << "Consuming ConcreteProductB2" << std::endl;
}
};
// 추상 팩토리
class AbstractFactory {
public:
virtual ~AbstractFactory() {}
virtual std::unique_ptr<AbstractProductA> createProductA() const = 0;
virtual std::unique_ptr<AbstractProductB> createProductB() const = 0;
};
// 구체화 팩토리 1
class ConcreteFactory1 : public AbstractFactory {
public:
std::unique_ptr<AbstractProductA> createProductA() const override {
return std::make_unique<ConcreteProductA1>();
}
std::unique_ptr<AbstractProductB> createProductB() const override {
return std::make_unique<ConcreteProductB1>();
}
};
// 구체화 팩토리 2
class ConcreteFactory2 : public AbstractFactory {
public:
std::unique_ptr<AbstractProductA> createProductA() const override {
return std::make_unique<ConcreteProductA2>();
}
std::unique_ptr<AbstractProductB> createProductB() const override {
return std::make_unique<ConcreteProductB2>();
}
};
// 클라이언트
class Client {
public:
Client(std::unique_ptr<AbstractFactory> factory)
: productA(factory->createProductA()), productB(factory->createProductB())
{}
void run() const {
productA->use();
productB->consume();
}
private:
std::unique_ptr<AbstractProductA> productA;
std::unique_ptr<AbstractProductB> productB;
};
// 사용 예시
int main() {
std::unique_ptr<AbstractFactory> factory1 = std::make_unique<ConcreteFactory1>();
std::unique_ptr<AbstractFactory> factory2 = std::make_unique<ConcreteFactory2>();
Client client1(std::move(factory1));
Client client2(std::move(factory2));
client1.run();
client2.run();
return 0;
}
위 코드는 추상 팩토리 방법을 기초적으로 구현해 놓은 코드이다.
이 코드를 해석해서 동작을 이어보면 다음과 같이 이어지게 된다.
main문에서 AbstractFactory 형태의 유니크주소 를 가진 factory1을 생성한다. 그리고 make_unique 를 통해 자식클래스의 ConcreteFactory1을 넣어준다. 이로써
factory1은 AbstractFactory 형식이지만, 실질적으로 자식클래스인 ConcreteFactory1의 형태를 띄게된다.
그 후 Client 형태의 clinet1변수를 선언하고 여기에 factory1의 데이터의 주소 소유권을 연결해주며 생성자에 매개변수로 전달한다.
매개변수로 전달받은 ConcreteFactory1은, Client의 생성자에 따라 각각 ConcreteFactory1에 해당하는 값들인
ConcreteProductA1 의 use()와 COncreteProductB1의 consume() 를 가지게된다
그 후 client1.run() 을 통해 가지게된 동작부의 주소값에 접속하여 동작을 실행하게 된다.
즉 이러한 추상 팩토리 기법은 인터페이스 -> 생성부 -> 동작부 로 연결되어 코드가 실행되게 된다.
Client에서만 생성자를 넣어서 Client에 연결된 데이터의 동작부를 Client에 끌어오게 되는것이다.
'디자인 패턴' 카테고리의 다른 글
Obserever 옵저버 패턴 (0) | 2023.11.14 |
---|---|
싱글톤 패턴(Singleton) (0) | 2023.10.10 |
빌더 패턴(Builder Pattern) (0) | 2023.05.10 |