의도
- 상세화된 서브클래스를 정의하지 않고도 서로 관련성이 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스를 제공
다른 이름
- 키트
언제 쓰는가?
- 객체가 생성되거나 구성, 표현되는 방식과 무관하게 시스템을 독립적으로 만들고자 할 때
- 여러 제품군 중 하나를 선택해서 시스템을 설정해야 하고 한번 구상한 제품을 다른 것으로 대체할 수 있을 때
- 관련된 제품 객체들이 함께 사용되도록 설계되었고, 이 부분에 대한 제약이 외부에도 지켜지도록 하고 싶을 때
- 제품에 대한 클래스 라이브러리를 제공하고, 그들의 구현이 아닌 인터페이스를 노출시키고 싶을 때
구조
- AbstractFactory : 개념적 제품에 대한 객체를 생성하는 연산으로 인터페이스를 정의
- ConcreteFactory : 구체적인 제품에 대한 객체를 생성하는 연산을 구현
- ConcreteProduct : 구체적으로 팩토리가 생성할 객체를 정의, AbstractProduct가 정의하는 인터페이스를 구현
- Client : AbstractFactory와 AbstractProduct 클래스에 선언된 인터페이스를 사용
결과
- 구체적인 클래스를 분리 : 팩토리는 제품 객체를 생성하는 과정과 책임을 캡슐화한 것이기 때문에, 구체적인 구현 클래스가 사용자에게서 분리됨. 일반 프로그램은 추상 인터페이스를 통해서만 인스턴스를 조작함.
- 제품군을 쉽게 대체할 수 있도록 함 : 구체 팩토리의 클래스는 응용프로그램에서 한 번만 나타나기 때문에 응용프로그램이 사용할 구체 팩토리를 변경하기 쉬움. 추상 팩토리는 필요한 모든 것을 생성하기 때문에 전체 제품군을 한번에 변경 가능.
- 제품 사이의 일관성을 증진 : 하나의 군 안에 속한 제품 객체들이 함께 동작하도록 설계되었을 때, 응용프로그램은 한 번에 오직 한 군에서만 만든 객체를 사용하도록 함으로써 프로그램의 일관성을 갖도록 해야 함.
- 새로운 종류의 제품을 제공하기 어려움 : 새 제품을 위해 기존의 팩토리를 확장하기는 쉽지 않음. 새 제품이 등장하면 팩토리의 구현을 변경해야 하고, 이는 추상 팩토리와 모든 서브 클래스의 변경을 의미.
예제 코드
- 연습을 위해 직접 예제를 작성해봤습니다.
- 객체를 만드는데에 new를 사용하였으나 delete는 없습니다. 가독성을 위해 적지 않았습니다.
1. 일단 이 패턴을 사용하는 외부의 관점에서 먼저 확인해보겠습니다. 간단하게 main에 작성했습니다.
#include "AbstractFactory1.h"
int main()
{
SmallCarFactory small_car_factory;
BigCarFactory big_car_factory;
Car* car1 = CompanyWJ::CreateCar(small_car_factory);
Car* car2 = CompanyWJ::CreateCar(big_car_factory);
car1->ShowCarInfo();
car2->ShowCarInfo();
return 0;
}
- 외부의 관점으로 볼 때 작은 차를 만드는 Factory와 큰 차를 만드는 Factory를 만들어서 CreateCar라는 메서드에 넘겨주게 되면 각각 큰 차와 작은 차를 만들어 낼 수 있었습니다.
- 추상 팩토리의 핵심은 CreateCar 메서드에 넘겨주는 인자인 Factory가 어떤 Factory 인지에 따라 다른 차를 만들어 낼 수 있는 것입니다.
2. 그렇다면 Car와 Factory 클래스들이 어떻게 이루어져있는지 확인해보겠습니다. 또한 실제적으로 차를 만드는 CreateCar 메서드까지 확인합니다.
//AbstractFactory1.h
#pragma once
#include "CarComponents.h"
#include <iostream>
std::ostream& operator<<(std::ostream& o, CarBody::SizeType size_type);
class Car
{
public:
void ShowCarInfo()
{
using namespace std;
cout << "car_body size : " << car_body->GetSizeType() << endl;
cout << "handle size : " << handle->GetSize() << endl;
cout << "wheel radius : " << wheel->GetRadius() << endl << endl;
}
public:
void CombineWheel(Wheel* wheel);
void CombineHandle(Handle* handle);
void CombineCarBody(CarBody* car_body);
private:
Wheel* wheel = nullptr;
Handle* handle = nullptr;
CarBody* car_body = nullptr;
};
class CarFactory
{
public:
virtual class Car* CreateCar() { return new Car; }
virtual class Wheel* CreateWheel() = 0;
virtual class Handle* CreateHandle() = 0;
virtual class CarBody* CreateCarBody() = 0;
};
class SmallCarFactory : public CarFactory
{
public:
virtual class Wheel* CreateWheel();
virtual class Handle* CreateHandle();
virtual class CarBody* CreateCarBody();
};
class BigCarFactory : public CarFactory
{
public:
virtual class Wheel* CreateWheel();
virtual class Handle* CreateHandle();
virtual class CarBody* CreateCarBody();
};
class CompanyWJ
{
public:
static Car* CreateCar(CarFactory& car_factory);
};
//AbstractFactory1.cpp
#include <iostream>
#include "AbstractFactory1.h"
Car* CompanyWJ::CreateCar(CarFactory& car_factory)
{
Car* new_car = car_factory.CreateCar();
new_car->CombineCarBody(car_factory.CreateCarBody());
new_car->CombineHandle(car_factory.CreateHandle());
new_car->CombineWheel(car_factory.CreateWheel());
return new_car;
}
void Car::CombineWheel(Wheel* wheel)
{
this->wheel = wheel;
}
void Car::CombineHandle(Handle* handle)
{
this->handle = handle;
}
void Car::CombineCarBody(CarBody* car_body)
{
this->car_body = car_body;
}
Wheel* SmallCarFactory::CreateWheel()
{
Wheel* wheel = new Wheel;
wheel->SetRadius(10.0f);
return wheel;
}
Handle* SmallCarFactory::CreateHandle()
{
Handle* handle = new Handle;
handle->SetSize(10);
return handle;
}
CarBody* SmallCarFactory::CreateCarBody()
{
CarBody* car_body = new CarBody;
car_body->SetSizeType(CarBody::SizeType::small);
return car_body;
}
Wheel* BigCarFactory::CreateWheel()
{
Wheel* wheel = new Wheel;
wheel->SetRadius(20.0f);
return wheel;
}
Handle* BigCarFactory::CreateHandle()
{
Handle* handle = new Handle;
handle->SetSize(20);
return handle;
}
CarBody* BigCarFactory::CreateCarBody()
{
CarBody* car_body = new CarBody;
car_body->SetSizeType(CarBody::SizeType::big);
return car_body;
}
std::ostream& operator<<(std::ostream& o, CarBody::SizeType size_type)
{
switch (size_type)
{
case CarBody::SizeType::small:
o << "small";
break;
case CarBody::SizeType::middle:
o << "middle";
break;
case CarBody::SizeType::big:
o << "big";
break;
}
return o;
}
- 각각의 factory들은 차의 부품들을 만드는 인터페이스를 가지고 있습니다.
- 어떤 부품을 만들지는 상속을 통해 오버라이딩을 통해 구체화해줍니다. 예제에서는 단순히 변수의 값을 조정하는 정도로 구성했습니다.
- 실질적으로 CreateCar 메서드에서 부르는 factory의 메서드들은 어떤 인스턴스를 인자로 받는지에 따라 호출되는 메서드가 달라지게 됩니다. 같은 인터페이스를 사용하지만 다른 인스턴스를 받음에 따라 동적으로 내용이 바뀔 수 있는 것, 이것이 핵심입니다.
3. 추가로 CarBody, Wheel, Handle 등 부가적인 클래스도 첨부합니다. 단순 getter, setter로만 이루어진 클래스들입니다.
//CarComponents.h
#pragma once
class Wheel
{
public:
void SetRadius(float radius) { this->radius = radius; }
float GetRadius() { return radius; }
private:
float radius;
};
class Handle
{
public:
void SetSize(size_t size) { this->size = size; }
size_t GetSize() { return size; }
private:
size_t size;
};
class CarBody
{
public:
enum class SizeType
{
small,
middle,
big
};
void SetSizeType(SizeType size_type) { this->size_type = size_type; }
SizeType GetSizeType() { return size_type; }
private:
SizeType size_type;
};
'디자인 패턴 > 생성' 카테고리의 다른 글
단일체(Singleton) (0) | 2023.12.19 |
---|---|
원형(Prototype) (0) | 2023.12.19 |
팩토리 메서드(Factory Method) (2) | 2023.12.18 |
빌더(Builder) (2) | 2023.12.17 |
생성 패턴 (0) | 2023.12.15 |