Factory Method Patterns 는 슈퍼클래스에서 객체를 생성하기 위한 인터페이스를 제공하지만, 서브클래스가 생성될 객체의 유형을 변경할 수 있도록 허용합니다. 팩토리 메서드는 구체적인 클래스를 지정하지 않고 제품 객체를 생성하는 문제를 해결하는 창조적인 디자인 패턴입니다. 팩토리 메서드는 직접 생성자 호출(new 키워드 연산자)을 사용하는 대신 객체를 생성하는 데 사용해야 하는 메서드를 정의합니다. 서브클래스는 이 메서드를 재정의하여 생성될 객체의 클래스를 변경할 수 있습니다.
사용 예시: Factory 메서드 패턴은 C# 코드에서 널리 사용됩니다. 코드에 높은 수준의 유연성을 제공해야 할 때 매우 유용합니다.
식별: 팩토리 메서드는 구체적인 클래스로부터 객체를 생성하는 생성 메서드로 인식할 수 있습니다. 객체를 생성하는 동안 구체적인 클래스가 사용되지만, 팩토리 메서드의 반환 유형은 일반적으로 추상 클래스 또는 인터페이스로 선언됩니다.
샘플예제
Factory Method 패턴을 이용하여 StarCraft 게임의 유닛을 생성하고 싸워서 없애는 간단한 예제 코드를 드리겠습니다. 이 예제에서는 Terran과 Protoss 두 종족의 유닛을 생성하고, 서로 공격하여 HP가 0이 되면 삭제됩니다.
using System;
// 각 유닛의 공통 기능을 정의할 IUnit 인터페이스
public interface IUnit
{
string Name { get; }
int HP { get; }
int AttackPower { get; }
void Attack(IUnit enemy);
void ReceiveDamage(int damage);
}
// Factory Method를 정의할 UnitCreator 클래스를 작성
public abstract class UnitCreator
{
public abstract IUnit CreateUnit(string unitType);
}
// IUnit 인터페이스를 구현한 Terran, Protoss 유닛
public class Terran : IUnit
{
public Terran(string name, int hp, int attackPower)
{
Name = name;
HP = hp;
AttackPower = attackPower;
}
public string Name { get; }
public int HP { get; private set; }
public int AttackPower { get; }
public void Attack(IUnit enemy)
{
enemy.ReceiveDamage(AttackPower);
}
public void ReceiveDamage(int damage)
{
HP -= damage;
}
}
public class Protoss : IUnit
{
public Protoss(string name, int hp, int attackPower)
{
Name = name;
HP = hp;
AttackPower = attackPower;
}
public string Name { get; }
public int HP { get; private set; }
public int AttackPower { get; }
public void Attack(IUnit enemy)
{
enemy.ReceiveDamage(AttackPower);
}
public void ReceiveDamage(int damage)
{
HP -= damage;
}
}
// UnitCreator를 상속받아 유닛을 생성하는 TerranCreator, ProtossCreator 클래스를 작성
public class TerranCreator : UnitCreator
{
public override IUnit CreateUnit(string unitType)
{
switch (unitType)
{
case "Marine":
return new Terran("Marine", 40, 5);
case "Marauder":
return new Terran("Marauder", 100, 10);
default:
throw new ArgumentException("Invalid unit type specified");
}
}
}
public class ProtossCreator : UnitCreator
{
public override IUnit CreateUnit(string unitType)
{
switch (unitType)
{
case "Zealot":
return new Protoss("Zealot", 60, 8);
case "Stalker":
return new Protoss("Stalker", 80, 12);
default:
throw new ArgumentException("Invalid unit type specified");
}
}
}
// 유닛을 생성하고 공격을 진행한 후 삭제함.
public class Game
{
public static void Main(string[] args)
{
UnitCreator terranCreator = new TerranCreator();
UnitCreator protossCreator = new ProtossCreator();
IUnit terranUnit = terranCreator.CreateUnit("Marine");
IUnit protossUnit = protossCreator.CreateUnit("Zealot");
// 유닛들이 서로 싸우기 시작합니다.
while (true)
{
terranUnit.Attack(protossUnit);
if (protossUnit.HP <= 0)
{
Console.WriteLine($"{protossUnit.Name} is defeated!");
break;
}
protossUnit.Attack(terranUnit);
if (terranUnit.HP <= 0)
{
Console.WriteLine($"{terranUnit.Name} is defeated!");
break;
}
}
}
}
Factory Method 패턴과 Abstract Factory 패턴 유사점
Factory Method 패턴과 Abstract Factory 패턴은 서로 매우 유사한 패턴이지만, 각 패턴의 목적과 적용 방법에 약간의 차이가 있습니다. 이 차이점을 설명드리겠습니다.
Factory Method 패턴:
객체 생성에 관련된 코드를 별도의 클래스로 분리하여, 생성할 객체와 해당 객체를 생성하기 위한 팩토리 메서드를 동일한 인터페이스에 정의합니다. 팩토리 메서드는 일반적으로 하나의 생성자를 가지고 있어, 추상화된 인터페이스를 구현하는 구체 클래스들 중 하나를 선택하여 생성합니다. 이 패턴은 새로운 종류의 객체를 추가하기 쉽게 만들어 기존의 코드에 최소한의 수정으로 새로운 유형의 객체를 지원할 수 있게 하려는 것이 목표입니다.
Abstract Factory 패턴:
추상 팩토리는 서로 관련된 객체 집합을 생성하기 위한 인터페이스를 제공합니다. 각 객체 집합은 일반적으로 동일한 주제나 용도로 연결되어 있습니다. 구체 팩토리 클래스는 이 인터페이스를 구현하여 해당 주제 또는 용도에 대한 객체 집합을 생성하는 로직을 제공합니다. 이 패턴은 서로 다른 객체 패밀리를 자유롭게 대체할 수 있는 유연성을 제공하려는 것이 목표입니다. 패턴에 어떤 클래스 구조와 코드가 포함되냐에 따라 Factory Method와 Abstract Factory를 구별할 수 있습니다. 많은 경우에서 이 두 패턴은 유사한 목적과 구조를 지니고 있기 때문에 상황에 맞게 선택하여 사용하시면 됩니다. 중요한 것은 패턴 자체의 이름이나 디자인보다는 클린하고 유연한 코드를 작성하는 데 도움이 되는 소프트웨어 디자인 원칙을 이해하는 것입니다.