의도

  • 객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스가 내리도록 함

 

다른 이름

  • 가상 생성자(Virtual Constructor)

 

언제 쓰는가?

  • 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때
  • 생성할 객체를 기술하는 책임을 자신의 서브클래스가 지정했으면 할 때
  • 객체 생성의 책임을 몇 개의 보조 서브클래스 가운데 하나에게 위임하고, 어떤 서브클래스가 위임자인지에 대한 정보를 국소화시키고 싶을 때

 

구조

GoF의 디자인 패턴 중 P.158

  • Product : 팩토리 메서드가 생성하는 객체의 인터페이스를 정의
  • Concrete Product : Product 클래스에 정의된 인터페이스를 실제로 구현
  • Creator : Product 타입의 객체를 반환하는 팩토리 메서드를 선언. Createor 클래스는 팩토리 메서드를 기본적으로 구현하는데, 이 구현에서는 Concrete Product 객체를 반환. 또한 Product 객체의 생성을 위해 팩토리 메서드를 호출
  • Concrete Creator : 팩토리 메서드를 재정의하여 Concrete Product의 인스턴스를 반환

 

결과

  1. 서브클래스에 대한 훅(hook) 메서드를 제공 : 객체별로 서로 다른 버전을 제공하는 훅 기능을 정의함.
  2. 병렬적인 클래스  계통을 연결하는 역할을 담당 : Creator는 Product를 만들 수 있는 메서드를 제공하는 입구를 제공하는 한편 Product의 서브클래스가 늘어남에 따라 Creator의 서브클래스도 그에 맞춰 늘어남

 

구현

  1. Creator가 추상 클래스여서 기본 구현을 제공하지 않는 경우와 Creator가 구체 클래스여서 기본 구현을 제공하는 경우가 있음
  2. 팩토리 메서드를 매개변수화 : 매개변수를 받아 어떤 Product를 만들지 식별하는 방법도 있음
  3. 지연 초기화를 사용할 수도 있음
  4. 템플릿을 사용하여 서브클래싱을 피하기도 함

 

예제 코드 ( 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;
}

main 실행 결과, TurnOn 메서드

  • 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

+ Recent posts