1. 팩토리 패턴(Factory Pattern)이란?
- 객체를 생성하는 클래스를 따로 두는 디자인 패턴(Simple Factory Pattern)
#include <iostream>
#include <memory>
using namespace std;
class Animal {
public:
virtual void makeSound() = 0;
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Woof!" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Meow!" << endl;
}
};
class AnimalFactory {
public:
static unique_ptr<Animal> createAnimal(const string& type) {
if (type == "Dog") return make_unique<Dog>();
else if (type == "Cat") return make_unique<Cat>();
return nullptr;
}
};
int main() {
auto dog = AnimalFactory::createAnimal("Dog");
dog->makeSound();
auto cat = AnimalFactory::createAnimal("Cat");
cat->makeSound();
return 0;
}
1) 팩토리 패턴의 장점
1. 객체 생성과 사용 분리
- 클라이언트 코드는 객체 생성 로직을 몰라도 되서, 코드의 결합도가 낮아지고 유지보수성이 향상
2. 코드 중복 최소화
- 객체 생성 코드가 한 곳(팩토리)에 집중되므로, 동일한 객체를 여러 번 만들 필요가 없음
3. 유연성 및 확장성 증가
- 새로운 클래스 타입을 추가하더라도 기존 코드에 영향을 주지 않음(Open/Closed 원칙 OCP)에 부합
4. 조건부 로직 감소
- 팩토리는 요청에 따라 적절한 객체를 반환하므로, 클라이언트 쪽에서 많은 조건부 로직(if-else 또는 switch-case 문 등)을 사용하지 않아도 됨
2) 팩토리 패턴의 단점
1. 클래스 수 증가
- 각각의 제품마다 별도의 팩토리 클래스를 만들어야 할 수 있으므로, 시스템 내 클래스 수가 증가
2. 코드 복잡성 증가
- 초기 학습 곡선이 가파르며, 설계 자체가 복잡해져서 이해하기 어려울 수 있음
3. 클래스 타입 변경 시 수정 필요
- 제공하는 제품(객체) 종류나 방식이 변경될 경우, 해당 팩토리 클래스를 수정해야함.
2. 팩토리 메소드 패턴(Factory Method Pattern)
- 객체를 생성하는 공장 메소드를 인터페이스로 정의하고, 서브 클래스에서 이 메소드를 오버라이딩하여 구체적인 객체를 생성하는 디자인 패턴
#include <iostream>
#include <memory>
using namespace std;
class Animal {
public:
virtual void makeSound() = 0;
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Woof!" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Meow!" << endl;
}
};
class AnimalCreator {
public:
virtual unique_ptr<Animal> createAnimal() = 0;
};
class DogCreator : public AnimalCreator {
public:
unique_ptr<Animal> createAnimal() override {
return make_unique<Dog>();
}
};
class CatCreator : public AnimalCreator {
public:
unique_ptr<Animal> createAnimal() override {
return make_unique<Cat>();
}
};
int main() {
DogCreator dog_creator;
auto dog = dog_creator.createAnimal();
dog->makeSound();
CatCreator cat_creator;
auto cat = cat_creator.createAnimal();
cat->makeSound();
return 0;
}
1) Factory Method Pattern을 사용하는 경우
1. 객체가 생성되는 과정이 복잡하거나 클라이언트에게 노출되어서는 안 될 때
- 팩토리 메소드를 통해 이를 캡슐화할 수 있으며, 클라이언트는 단순히 팩토리 메소드를 호출하기만 하면 되므로, 내부 로직에 대해 알 필요가 없음
2. 객체 타입을 실행 시간에 결정해야 할 때
- 팩토리 메소드는 실행 시간에 어떤 클래스의 인스턴스를 생성할지 결정할 수 있음. 따라서 프로그램의 동작을 런타임에서 변경하고 확장하는 것이 가능
3. 코드의 결합도를 낮추고 싶을 때
- 객체 생성 코드와 사용 코드 사이의 의존성을 줄여줌. 이로 인해 한 부분의 변경이 다른 부분에 영향을 미치는 정도가 줄어들며, 유지보수와 테스트가 용이
3. 추상 팩토리 패턴(Abstract Factory Pattern)
- 관련된 객체의 집합을 생성하기 위한 인터페이스를 제공하는 디자인 패턴
#include <iostream>
#include <memory>
using namespace std;
class Animal {
public:
virtual void makeSound() = 0;
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Woof!" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Meow!" << endl;
}
};
class Food {
public:
virtual void eat() = 0;
};
class DogFood : public Food {
public:
void eat() override {
cout << "Dog is eating dog food." << endl;
}
};
class CatFood : public Food {
public:
void eat() override {
cout << "Cat is eating cat food." << endl;
}
};
// Abstract Factories
class AnimalFactory {
public:
virtual unique_ptr<Animal> createAnimal() = 0;
};
class FoodFactory {
public:
virtual unique_ptr<Food> createFood() = 0;
};
// Concrete Factories
class DogFactory : public AnimalFactory {
public:
unique_ptr<Animal> createAnimal() {
return make_unique<Dog>();
}
};
class CatFactory : public AnimalFactory {
public:
unique_ptr<Animal> createAnimal() {
return make_unique<Cat>();
}
};
class DogFoodFactory : public FoodFactory {
public:
unique_ptr<Food> createFood() {
return make_unique<DogFood>();
}
};
class CatFoodFactory : public FoodFactory {
public:
unique_ptr<Food> createFood() {
return make_unique<CatFood>();
}
};
class PetFactory {
public:
static void create_pet(AnimalFactory& Af, FoodFactory& Ff) {
unique_ptr<Animal> pet = Af.createAnimal();
pet->makeSound();
unique_ptr<Food> pet_food = Ff.createFood();
pet_food->eat();
}
};
int main() {
DogFactory df;
DogFoodFactory dff;
PetFactory::create_pet(df, dff);
CatFactory cf;
CatFoodFactory cff;
PetFactory::create_pet(cf, cff);
return 0;
}
1) 추상 팩토리 패턴을 사용하는 경우
1. 관련된 객체의 집합을 생성할 때
- 추상 팩토리 패턴은 관련된 객체들을 함꼐 생성하는 것에 중점을 두는 패턴. 예를 들어, GUI 애플리케이션에서 여러 플랫폼(Windows, Linux 등)에 대한 일관된 사용자 인터페이스를 제공해야 하는 경우 각 플랫폼별로 버튼, 메뉴 등의 위젯을 생성하는 별도의 팩토리 클래스 생성 가능
2. 시스템이 독립적으로 확장 가능해야 할 때
- 추상 팩토리 패턴은 새로운 종류의 제품을 추가하기 쉽게 가능. 새로운 제품군(즉, 관련된 객체들의 집합)이 추가되더라도 기존 코드는 변경할 필요가 없으며, 단지 새로운 팩토리 클래스만 추가하면 됩니다.
3. 클래스 라이브러리를 제공하고, 그 구현 내용을 감춰야 할 때
- 클라이언트 코드는 추상 팩토리와 추상 제품 인터페이스만 알고 있으면 되므로 구현 세부사항에 대해서는 신경 쓸 필요가 없음
4. 제공되는 클래스들이 서로 연관되거나 의존성을 가질 때
- 한 객체가 다른 객체 없이는 의미가 없거나 정상적으로 동작하지 않는 경우, 추상 팩토리 패턴을 통해 연관성 잇는 객체들을 함꼐 생성하여 오류 가능성을 줄일 수 있
'CS 전공지식 정리 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] 옵저버 패턴(Observer Pattern) (0) | 2023.10.22 |
---|---|
[디자인 패턴] 전략 패턴(Strategy Pattern) (1) | 2023.10.22 |
[디자인 패턴] 싱글톤 패턴(Singleton Pattern) (0) | 2023.06.23 |