의도
- 객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스가 내리도록 함
다른 이름
- 가상 생성자(Virtual Constructor)
언제 쓰는가?
- 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때
- 생성할 객체를 기술하는 책임을 자신의 서브클래스가 지정했으면 할 때
- 객체 생성의 책임을 몇 개의 보조 서브클래스 가운데 하나에게 위임하고, 어떤 서브클래스가 위임자인지에 대한 정보를 국소화시키고 싶을 때
구조
- Product : 팩토리 메서드가 생성하는 객체의 인터페이스를 정의
- Concrete Product : Product 클래스에 정의된 인터페이스를 실제로 구현
- Creator : Product 타입의 객체를 반환하는 팩토리 메서드를 선언. Createor 클래스는 팩토리 메서드를 기본적으로 구현하는데, 이 구현에서는 Concrete Product 객체를 반환. 또한 Product 객체의 생성을 위해 팩토리 메서드를 호출
- Concrete Creator : 팩토리 메서드를 재정의하여 Concrete Product의 인스턴스를 반환
결과
- 서브클래스에 대한 훅(hook) 메서드를 제공 : 객체별로 서로 다른 버전을 제공하는 훅 기능을 정의함.
- 병렬적인 클래스 계통을 연결하는 역할을 담당 : Creator는 Product를 만들 수 있는 메서드를 제공하는 입구를 제공하는 한편 Product의 서브클래스가 늘어남에 따라 Creator의 서브클래스도 그에 맞춰 늘어남
구현
- Creator가 추상 클래스여서 기본 구현을 제공하지 않는 경우와 Creator가 구체 클래스여서 기본 구현을 제공하는 경우가 있음
- 팩토리 메서드를 매개변수화 : 매개변수를 받아 어떤 Product를 만들지 식별하는 방법도 있음
- 지연 초기화를 사용할 수도 있음
- 템플릿을 사용하여 서브클래싱을 피하기도 함
예제 코드 ( 1 )
- 첫 번째 예제는 Creator (ComputerDeviceMaker)를 추상 클래스로 정의하여 순수 가상 함수 Create를 서브 클래스에서 재정의하게 끔 하여 다양한 ComputerDevice(Product)를 만들 수 있게끔 하는 팩토리 메서드의 정석적인 사용 예제
#include "FactoryMethod1.h"
#include <vector>
int main()
{
std::vector<ComputerDevice*> computer_devices;
MonitorMaker montitor_maker;
KeyboardMaker keyboard_maker;
MouseMaker mouse_maker;
computer_devices.push_back(montitor_maker.Create());
computer_devices.push_back(keyboard_maker.Create());
computer_devices.push_back(mouse_maker.Create());
for (ComputerDevice* cd : computer_devices)
{
cd->TurnOn();
}
return 0;
}
- ComputerDeviceMaker를 상속받은 서브클래스들 MonitorMaker, KeyboardMaker, MouseMaker들은 Create 메서드를 사용하여 다른 인스턴스(모니터, 키보드, 마우스)를 만들어내고 있음
//FactoryMethod1.h
#include "FactoryMethod1_Components.h"
class ComputerDeviceMaker
{
public:
virtual ComputerDevice* Create() = 0;
};
class MonitorMaker : public ComputerDeviceMaker
{
public:
virtual ComputerDevice* Create()
{
return new Monitor;
}
};
class MouseMaker : public ComputerDeviceMaker
{
public:
virtual ComputerDevice* Create()
{
return new Mouse;
}
};
class KeyboardMaker : public ComputerDeviceMaker
{
public:
virtual ComputerDevice* Create()
{
return new Keyboard;
}
};
- 어떤 인스턴스를 만들지만 정해서 return 해주면 되는 간단한 함수여서 헤더에서 다 정의했음
- 기본적으로 이 패턴이 사용될 수 있는 전제는 Create가 반환하는 Product의 인터페이스가 상속해주는 객체들만을 사용할 수 있다는 점
- 그렇기 때문에 Create가 반환하는 자료형으로서 이 인스턴스를 다루려면 캐스팅을 하지 않는 이상 인터페이스에 정의된 메서드만 부를 수 있게 됨
//FactorMethod1_Components.h
#pragma once
#include <string>
#include <iostream>
class ComputerDevice
{
public:
virtual void TurnOn() = 0;
};
class Mouse : public ComputerDevice
{
public:
virtual void TurnOn()
{
std::cout << name << "의 전원을 켭니다" << std::endl;
}
private:
std::string name = "Mouse";
};
class Keyboard : public ComputerDevice
{
public:
virtual void TurnOn()
{
std::cout << name << "의 전원을 켭니다" << std::endl;
}
private:
std::string name = "Keyboard";
};
class Monitor : public ComputerDevice
{
public:
virtual void TurnOn()
{
std::cout << name << "의 전원을 켭니다" << std::endl;
}
private:
std::string name = "Monitor";
};
예제 코드 ( 2 )
- 넘어오는 매개변수로 어떤 인스턴스를 만들지를 식별하는 예제
#include "FactoryMethod2.h"
int main()
{
ComputerDeviceMaker_V2 cd_maker;
ComputerDevice* cd1 = cd_maker.Create(DeviceType::Keyboard);
ComputerDevice* cd2 = cd_maker.Create(DeviceType::Monitor);
ComputerDevice* cd3 = cd_maker.Create(DeviceType::Mouse);
cd1->TurnOn();
cd2->TurnOn();
cd3->TurnOn();
return 0;
}
- Create 메서드에 어떤 인스턴스를 만들지 식별하는 매개변수를 받게 끔 구성하여 cd1, cd2, cd3가 각각 다른 인스턴스로 생성됨
//FactoryMethod2.h
#pragma once
#include "FactoryMethod2_Components.h"
enum class DeviceType
{
Monitor,
Mouse,
Keyboard
};
class ComputerDeviceMaker_V2
{
public:
ComputerDevice* Create(DeviceType device_type)
{
switch (device_type)
{
case DeviceType::Monitor:
return new Monitor;
break;
case DeviceType::Mouse:
return new Mouse;
break;
case DeviceType::Keyboard:
return new Keyboard;
break;
}
}
};
- 어떤 인스턴스를 만들지 식별하기 위한 enum class를 만들어주고 switch 문을 통해 다른 인스턴스를 만듬
예제 코드 ( 3 )
- 템플릿을 사용하여 서브클래스가 많아지는 것을 방지
#include "FactoryMethod3.h"
int main()
{
ComputerDeviceMaker_V3<Mouse> mouse_maker;
ComputerDeviceMaker_V3<Keyboard> keyboard_maker;
ComputerDeviceMaker_V3<Monitor> monitor_maker;
Mouse* mouse = mouse_maker.Create();
Keyboard* keyboard = keyboard_maker.Create();
Monitor* monitor = monitor_maker.Create();
mouse->TurnOn();
keyboard->TurnOn();
monitor->TurnOn();
return 0;
}
- ComputerDeviceMaker가 서로 다른 템플릿 인자를 받음으로서 Create 메서드가 동작하는 방식이 달라짐
- ComputerDeviceMaker가 MouseMaker, KeyboardMaker, MonitorMaker 등의 서브클래스로 분화하지 않아도 되는 결과
//TemplateMethod3.h
#pragma once
#include "FactoryMethod3_Components.h"
#include <type_traits>
template<typename T>
class ComputerDeviceMaker_V3
{
public:
ComputerDeviceMaker_V3()
{
static_assert(std::is_base_of_v<ComputerDevice, T>, "T is not derived from ComputerDevice Class");
}
public:
T* Create()
{
return new T;
}
};
- 생성자에서 템플릿 인자로 넘어오는 T에 대해 ComputerDevice를 상속받은 클래스가 아니라면 빌드가 안 되게끔 막았음
- Create의 반환 값은 T를 반환함으로 인터페이스로서가 아니라 인스턴스 자신의 클래스를 타입으로 가질 수 있게끔 하였음
'디자인 패턴 > 생성' 카테고리의 다른 글
단일체(Singleton) (0) | 2023.12.19 |
---|---|
원형(Prototype) (0) | 2023.12.19 |
빌더(Builder) (2) | 2023.12.17 |
추상 팩토리(Abstract Factory) (0) | 2023.12.15 |
생성 패턴 (0) | 2023.12.15 |