의도

  • 상세화된 서브클래스를 정의하지 않고도 서로 관련성이 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스를 제공

 

다른 이름

  • 키트

 

언제 쓰는가?

  • 객체가 생성되거나 구성, 표현되는 방식과 무관하게 시스템을 독립적으로 만들고자 할 때
  • 여러 제품군 중 하나를 선택해서 시스템을 설정해야 하고 한번 구상한 제품을 다른 것으로 대체할 수 있을 때
  • 관련된 제품 객체들이 함께 사용되도록 설계되었고, 이 부분에 대한 제약이 외부에도 지켜지도록 하고 싶을 때
  • 제품에 대한 클래스 라이브러리를 제공하고, 그들의 구현이 아닌 인터페이스를 노출시키고 싶을 때

 

구조

GoF Design Pattern P.134

  • AbstractFactory : 개념적 제품에 대한 객체를 생성하는 연산으로 인터페이스를 정의
  • ConcreteFactory : 구체적인 제품에 대한 객체를 생성하는 연산을 구현
  • ConcreteProduct : 구체적으로 팩토리가 생성할 객체를 정의, AbstractProduct가 정의하는 인터페이스를 구현
  • Client : AbstractFactory와 AbstractProduct 클래스에 선언된 인터페이스를 사용

 

결과

  1. 구체적인 클래스를 분리 : 팩토리는 제품 객체를 생성하는 과정과 책임을 캡슐화한 것이기 때문에, 구체적인 구현 클래스가 사용자에게서 분리됨. 일반 프로그램은 추상 인터페이스를 통해서만 인스턴스를 조작함.
  2. 제품군을 쉽게 대체할 수 있도록 함 : 구체 팩토리의 클래스는 응용프로그램에서 한 번만 나타나기 때문에 응용프로그램이 사용할 구체 팩토리를 변경하기 쉬움. 추상 팩토리는 필요한 모든 것을 생성하기 때문에 전체 제품군을 한번에 변경 가능.
  3. 제품 사이의 일관성을 증진 : 하나의 군 안에 속한 제품 객체들이 함께 동작하도록 설계되었을 때, 응용프로그램은 한 번에 오직 한 군에서만 만든 객체를 사용하도록 함으로써 프로그램의 일관성을 갖도록 해야 함.
  4. 새로운 종류의 제품을 제공하기 어려움 : 새 제품을 위해 기존의 팩토리를 확장하기는 쉽지 않음. 새 제품이 등장하면 팩토리의 구현을 변경해야 하고, 이는 추상 팩토리와 모든 서브 클래스의 변경을 의미.

 

예제 코드

  • 연습을 위해 직접 예제를 작성해봤습니다.
  • 객체를 만드는데에 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

+ Recent posts