“팩토리 패턴(Factory Pattern)”은 객체 생성 로직을 별도의 “팩토리”(메서드·클래스·인터페이스 등)로 캡슐화하여,
클라이언트 코드가 특정 구체 클래스에 직접 의존하지 않도록 하는 생성(Creational) 디자인 패턴 계열을 가리킨다.
대표적인 종류
- Factory Method 패턴
- Abstract Factory 패턴
- (확장) Simple Factory(심플 팩토리) 라고 불리는 간단한 변형도 많이 사용됨.
어떤 클래스 내부(또는 클라이언트 코드)에서 직접 new()를 사용해 구체 클래스 객체를 직접 생성하면, 코드가 특정 구현 클래스에 강하게 결합(coupling)된다.
예를 들어 Document doc = new WordDocument(); 라는 부분이 있을 때, 나중에 해당 부분을 PdfDocument로 교체하고 싶다면 관련 코드를 찾아서 전부 수정해야 한다.
새로운 제품 클래스가 추가되거나, 기존 생성 로직이 바뀌면, 객체를 생성하는 부분마다 수정이 필요하다.
이로 인해 OCP(Open/Closed Principle), DIP(Dependency Inversion Principle) 등을 지키기 어려워진다.
따라서 객체 생성 로직을 한곳(팩토리)에만 두면, 생성 방식 변경 시 이 로직만 수정하면 되므로 유지보수가 편해진다.
“팩토리 메서드” 패턴은 추상 클래스(또는 인터페이스)가 객체 생성을 위한 추상 메서드(또는 오버라이드 가능한 메서드)를 정의하고,
이 메서드를 구상 클래스(서브클래스)에서 구현(오버라이드)하여 구체 객체를 생성하도록 하는 구조이다.
즉, “어떤 객체를 생성할지?”에 대한 책임을 하위 클래스에게 위임하므로, 클라이언트는 추상 팩토리 메서드만 알고 있으면 된다.
TextApplication vs DrawingApplication), 게임 지역별 몬스터(ForestSpawner vs CaveSpawner) 등.Creator)만 두고, 사용자가 커스텀 서브클래스에서 생성 방식을 확장/재정의CreateProduct())를 정의.return new ConcreteProductA();).// (1) 구상 Creator 클래스 사용될 인터페이스 & 구현 클래스
public interface IDocument
{
void Open();
void Close();
void Save();
}
public class WordDocument : IDocument { /* ... */ }
public class PdfDocument : IDocument { /* ... */ }
// (2) 추상 Creator 클래스
public abstract class Application
{
protected abstract IDocument CreateDocument(); // 팩토리 메서드
public IDocument NewDocument()
{
IDocument doc = CreateDocument();
doc.Open();
return doc;
}
public void SaveDocument(IDocument doc) => doc.Save();
}
// (3) 구상 Creator 클래스
public class WordApplication : Application
{
protected override IDocument CreateDocument() => new WordDocument();
}
public class PdfApplication : Application
{
protected override IDocument CreateDocument() => new PdfDocument();
}
// (4) 사용
class Program
{
static void Main()
{
Application wordApp = new WordApplication();
IDocument wordDoc = wordApp.NewDocument();
wordApp.SaveDocument(wordDoc);
Application pdfApp = new PdfApplication();
IDocument pdfDoc = pdfApp.NewDocument();
pdfApp.SaveDocument(pdfDoc);
}
}
WordApplication이 내부적으로 WordDocument를 생성하게 되므로, 클라이언트는 직접 WordDocument를 new 할 필요가 없다.// (1) 구상 Creator 클래스 사용될 인터페이스 & 구현 클래스
public interface IMonster
{
void Spawn();
void Attack();
}
public class Slime : IMonster { /* ... */ }
public class Goblin : IMonster { /* ... */ }
// (2) 추상 Creator 클래스
public abstract class MonsterSpawner
{
protected abstract IMonster CreateMonster(); // 팩토리 메서드
public void SpawnMonster()
{
IMonster monster = CreateMonster();
monster.Spawn();
monster.Attack();
}
}
// (3) 구상 Creator 클래스
public class ForestSpawner : MonsterSpawner
{
protected override IMonster CreateMonster() => new Slime();
}
public class CaveSpawner : MonsterSpawner
{
protected override IMonster CreateMonster() => new Goblin();
}
// (4) 사용
public class GameFactoryExample
{
public static void Main()
{
MonsterSpawner forest = new ForestSpawner();
MonsterSpawner cave = new CaveSpawner();
forest.SpawnMonster(); // 슬라임이 등장
cave.SpawnMonster(); // 고블린이 등장
}
}
new를 쓰는 편이 더 직관적일 수 있음.추상 팩토리 패턴은 “서로 연관된 (또는 의존 관계가 있는) 복수의 객체(제품군)를 한꺼번에 생성”해야 할 때 사용하는 패턴.
예: Windows UI 세트(WindowsButton + WindowsCheckbox), Mac UI 세트(MacButton + MacCheckbox)처럼 서로 호환되는 객체들을 일관되게 만들고 싶을 때 사용.
CreateButton(), CreateCheckbox() 등)를 정의.IButton, ICheckbox).// (1) 인터페이스
public interface IButton
{
void Render();
}
public interface ICheckbox
{
void Render();
}
// (2) 구체 제품
public class WindowsButton : IButton
{
public void Render() => Console.WriteLine("Windows 버튼");
}
public class MacButton : IButton
{
public void Render() => Console.WriteLine("Mac 버튼");
}
public class WindowsCheckbox : ICheckbox
{
public void Render() => Console.WriteLine("Windows 체크박스");
}
public class MacCheckbox : ICheckbox
{
public void Render() => Console.WriteLine("Mac 체크박스");
}
// (3) 추상 팩토리
public interface IGUIFactory
{
IButton CreateButton();
ICheckbox CreateCheckbox();
}
// (4) 구체 팩토리
public class WindowsFactory : IGUIFactory
{
public IButton CreateButton() => new WindowsButton();
public ICheckbox CreateCheckbox() => new WindowsCheckbox();
}
public class MacFactory : IGUIFactory
{
public IButton CreateButton() => new MacButton();
public ICheckbox CreateCheckbox() => new MacCheckbox();
}
// (5) 사용
public class AbstractFactoryExample
{
private IButton _button;
private ICheckbox _checkbox;
public AbstractFactoryExample(IGUIFactory factory)
{
_button = factory.CreateButton();
_checkbox = factory.CreateCheckbox();
}
public void RenderUI()
{
_button.Render();
_checkbox.Render();
}
public static void Main()
{
IGUIFactory windowsFactory = new WindowsFactory();
AbstractFactoryExample winApp = new AbstractFactoryExample(windowsFactory);
winApp.RenderUI(); // Windows 버튼, Windows 체크박스
IGUIFactory macFactory = new MacFactory();
AbstractFactoryExample macApp = new AbstractFactoryExample(macFactory);
macApp.RenderUI(); // Mac 버튼, Mac 체크박스
}
}
“심플 팩토리”는 “정적 메서드 기반”의 간단한 팩토리 기법.
별도의 팩토리 클래스(또는 팩토리 메서드) 하나만 만들어놓고, CreateXXX() 같은 메서드 내부에서 if-else나 switch를 사용하여 구체 객체를 생성해 반환.
public static class DocumentFactory
{
public static IDocument CreateDocument(string type)
{
if(type == "Word")
return new WordDocument();
else if(type == "PDF")
return new PdfDocument();
else
throw new ArgumentException("Unknown Document Type");
}
}
// 사용
IDocument doc1 = DocumentFactory.CreateDocument("Word");
IDocument doc2 = DocumentFactory.CreateDocument("PDF");
CreateDocument("Word")처럼 사용.new를 직접 고치는 것보다는 낫다).if-else / switch 분기 로직이 많아지면, 코드가 비대해질 수 있음.| 패턴 | 목적 | 특징 |
|---|---|---|
| Factory Method | - “하나의 제품” 생성 로직을 서브클래스가 결정하도록 함 | |
| - 추상 Creator 클래스에서 팩토리 메서드를 정의 | - 추상 Creator & 구상 Creator 클래스 | |
| - 상속(오버라이딩) 기반의 구조 | ||
| Abstract Factory | - “서로 연관된 복수의 제품군”을 일관되게 생성 | - 여러 팩토리 메서드(버튼, 체크박스 등) |
| - 제품군의 일관성과 호환성 보장 | ||
| Simple Factory | - 별도 팩토리 클래스(혹은 정적 메서드)에서 간단히 객체 생성 로직을 캡슐화 | - 공통 분기(if-else/switch) 사용 |
new로 생성하는 게 더 나을 수도 있음.if-else나 switch로 구체 객체를 생성하는 간단한 방식.상황에 따라 각 패턴을 적절히 선택하면, 확장성·유지보수성을 높일 수 있다.