오늘은 Factory 패턴에 대해 알아보는 시간을 가지겠습니다.

- 객체 생성에 관한 디자인 패턴으로, 객체 생성 로직을 별도의 Factory(공장) 클래스로 분리하여 코드의 재사용성과 유지보수성을 높여줍니다.
- 객체를 직접 생성하는 대신 Factory를 통해 생성하면, 새로운 객체 타입이 추가되거나 기존 로직이 수정되더라도 다른 코드에 영향을 주지 않도록 설계할 수 있습니다.
- 객체 생성 로직의 캡슐화
- 객체 생성에 대한 로직을 클라이언트 코드에서 분리하여 Factory 클래스가 대신 담당합니다.
- 객체 타입에 대한 유연성
- Factory를 통해 생성되는 객체의 타입을 동적으로 결정할 수 있습니다.
- 의존성 감소
- 클라이언트가 객체의 구체적인 클래스에 의존하지 않고, 인터페이스나 추상 클래스에 의존하도록 설계됩니다.
- 코드 재사용성 증가
- 객체 생성 코드를 Factory 클래스에 집중시켜 중복 코드를 제거합니다.
- 객체 생성의 제어
- 객체 생성 과정을 제어하며, 복잡한 초기화 과정을 캡슐화할 수 있습니다.
- 확장성
- 새로운 객체 타입을 추가할 때 기존 코드 수정 없이 Factory 클래스만 수정하거나 확장이 가능합니다.
- Simple Factory (간단한 팩토리)
- 하나의 Factory 클래스가 모든 객체 생성을 담당
- Factory Method (팩토리 메서드)
- 객체 생성을 서브클래스에 위임
- Abstract Factory (추상 팩토리)
- 관련된 객체를 생성하는 인터페이스 제공
public interface IEnemy
{
void Attack();
}
- Factory가 생성할 객체의 부모 클래스 또는 인터페이스입니다.
public class Slime : IEnemy
{
public void Attack()
{
Debug.Log("Slime attacks by jumping!");
}
}
public class Goblin : IEnemy
{
public void Attack()
{
Debug.Log("Goblin attacks with a club!");
}
}
public class Dragon : IEnemy
{
public void Attack()
{
Debug.Log("Dragon attacks with fire breath!");
}
}
- 실제로 생성될 구체적인 객체입니다. 각 Attack 별로 다른 동작을 하게끔 구현합니다.
public class EnemyFactory
{
public static IEnemy CreateEnemy(string enemyType)
{
switch (enemyType)
{
case "Slime":
return new Slime();
case "Goblin":
return new Goblin();
case "Dragon":
return new Dragon();
default:
throw new System.ArgumentException("Invalid enemy type");
}
}
}
- 객체를 생성하고 반환하는 클래스입니다.
public class GameManager : MonoBehaviour
{
void Start()
{
// 적 생성
IEnemy slime = EnemyFactory.CreateEnemy("Slime");
slime.Attack();
IEnemy goblin = EnemyFactory.CreateEnemy("Goblin");
goblin.Attack();
IEnemy dragon = EnemyFactory.CreateEnemy("Dragon");
dragon.Attack();
}
}
- 테스트를 위한 클래스입니다. Factory에서 Enemy를 생성하고 난 뒤 Attack을 실행시켜줍니다.
장점
- 객체 생성 로직을 캡슐화하여 클라이언트 코드가 간단해집니다.
- 새로운 타입 추가 시 Factory만 수정하면 되므로 유지보수성이 증가
- 소규모 프로젝트나 간단한 객체 생성 로직을 처리할 때 유용합니다.
단점
- 모든 객체 생성 로직이 한 클래스에 집중되므로, 규모가 커지면 복잡도가 증가할 수 있음
- 새로운 타입 추가 시 Factory 클래스 수정이 필요하므로 Open-Closed Principle(OCP)을 완벽히 준수하지 못함.
- Open-Closed Principle (OCP) : 개방 폐쇄 원칙
public interface IEnemy
{
void Attack();
}
- 생성할 객체의 인터페이스 또는 추상 클래스이며 공통 인터페이스를 정의합니다.
public class Slime : IEnemy
{
public void Attack()
{
Debug.Log("Slime attacks by jumping!");
}
}
public class Goblin : IEnemy
{
public void Attack()
{
Debug.Log("Goblin attacks with a club!");
}
}
public class Dragon : IEnemy
{
public void Attack()
{
Debug.Log("Dragon attacks with fire breath!");
}
}
- Product를 구현한 실제 객체입니다.
public abstract class EnemyFactory
{
// Factory Method
public abstract IEnemy CreateEnemy();
// 공통 로직
public void SpawnEnemy()
{
IEnemy enemy = CreateEnemy();
enemy.Attack();
}
}
- Factory Method를 정의하는 추상 클래스 또는 인터페이스 입니다.
public class SlimeFactory : EnemyFactory
{
public override IEnemy CreateEnemy()
{
return new Slime();
}
}
public class GoblinFactory : EnemyFactory
{
public override IEnemy CreateEnemy()
{
return new Goblin();
}
}
public class DragonFactory : EnemyFactory
{
public override IEnemy CreateEnemy()
{
return new Dragon();
}
}
- Creator를 구현하거나 상속받아 구체적인 객체 생성 로직을 제공하는 클래스 입니다.
public class GameManager : MonoBehaviour
{
void Start()
{
EnemyFactory slimeFactory = new SlimeFactory();
slimeFactory.SpawnEnemy();
EnemyFactory goblinFactory = new GoblinFactory();
goblinFactory.SpawnEnemy();
EnemyFactory dragonFactory = new DragonFactory();
dragonFactory.SpawnEnemy();
}
}
- 어떤 적이 생성되는지 알 필요 없이 Factory Method를 호출하여 적을 생성하고 사용합니다.
장점
- 객체 생성과 사용 로직을 분리하여 코드가 간결해집니다.
- 새로운 객체 타입을 추가할 때, 기존 코드를 수정하지 않아도 됩니다.
- 다양한 서브클래스를 통해 객체 생성을 제어가 가능합니다.
- 클라이언트는 구체적인 객체를 몰라도 동작이 가능합니다.
단점
- 클래스 수가 늘어나면서 코드 관리가 어려워질 수 있습니다.
- 설계 단계에서 객체 타입과 Factory를 명확히 정의해야 합니다.
public interface IEnemy
{
void Attack();
}
public interface IWeapon
{
void Use();
}
- 관련된 객체 군의 생성을 위한 인터페이스를 정의합니다.
public class Slime : IEnemy
{
public void Attack()
{
Debug.Log("Slime attacks by jumping!");
}
}
public class SlimeWeapon : IWeapon
{
public void Use()
{
Debug.Log("Slime throws a sticky ball!");
}
}
public class Goblin : IEnemy
{
public void Attack()
{
Debug.Log("Goblin attacks with a club!");
}
}
public class GoblinWeapon : IWeapon
{
public void Use()
{
Debug.Log("Goblin swings its club!");
}
}
- Abstract Factory를 구현하여 구체적인 객체를 생성합니다.
- 2개의 객체만 구현하였습니다.
public interface IEnemyFactory
{
IEnemy CreateEnemy();
IWeapon CreateWeapon();
}
- 객체 군에 포함되는 각 객체의 인터페이스를 정의합니다.
public class SlimeFactory : IEnemyFactory
{
public IEnemy CreateEnemy()
{
return new Slime();
}
public IWeapon CreateWeapon()
{
return new SlimeWeapon();
}
}
public class GoblinFactory : IEnemyFactory
{
public IEnemy CreateEnemy()
{
return new Goblin();
}
public IWeapon CreateWeapon()
{
return new GoblinWeapon();
}
}
- Abstract Product를 구현한 실제 객체입니다.
public class GameManager : MonoBehaviour
{
void Start()
{
// Slime Factory 사용
IEnemyFactory slimeFactory = new SlimeFactory();
IEnemy slime = slimeFactory.CreateEnemy();
IWeapon slimeWeapon = slimeFactory.CreateWeapon();
slime.Attack();
slimeWeapon.Use();
// Goblin Factory 사용
IEnemyFactory goblinFactory = new GoblinFactory();
IEnemy goblin = goblinFactory.CreateEnemy();
IWeapon goblinWeapon = goblinFactory.CreateWeapon();
goblin.Attack();
goblinWeapon.Use();
}
}
- 추상 팩토리와 추상 제품의 인터페이스만 사용하여 객체를 생성 및 사용합니다.
장점
- 적과 무기처럼 관련된 객체들이 함께 생성되므로 객체 간의 일관성을 유지가 가능합니다.
- 새로운 객체 군이 추가될 때 기존 코드를 수정하지 않고 팩토리만 추가하면 됩니다.
- 객체 생성 로직이 팩토리 내부에 캡슐화되어 클라이언트 코드는 단순화됩니다.
단점
- 클래스와 인터페이스가 많아져 설계가 복잡해질 수 있습니다.
- 객체 군과 팩토리의 관계를 명확히 정의해야 합니다.
| 특징 | Simple Factory | Factory Method | Abstract Factory |
|---|---|---|---|
| 객체 생성 방식 | 팩토리 클래스에서 모든 객체를 생성 | 객체 생성 책임을 서브클래스로 위임 | 객체 군을 생성 |
| 확장성 | 낮음 (기존 클래스 수정 필요) | 중간 (새로운 서브클래스 추가로 확장 가능) | 높음 (새 객체 군 추가가 용이) |
| 유연성 | 낮음 | 중간 | 높음 |
| 객체 간 관계 | 없음 | 없음 | 객체 간의 관계를 고려한 생성 |
| 적합한 프로젝트 규모 | 소규모 | 중규모 | 대규모 |
| 사용 예시 | 단순 객체 생성 | 각 객체 타입별 팩토리 | 관련된 객체 군 생성 |
- 단순한 객체 생성이 필요하고, 객체 타입이 자주 변경이 안되며 소규모 프로젝트의 경우
Simple Factory- 객체 타입이 자주 추가되거나 변경될 가능성이 높고, 확장성과 유지보수성이 중요하며 중규모 이상의 프로젝트에서 객체를 유연하게 관리를 하려면
Factory Method- 객체들이 군으로 묶여서 관리되고 여러 객체 간의 관계를 유지해야 하며 대규모 프로젝트나 테마별,그룹별로 객체를 관리해야 한다면
- Abstract Factory

- 각 팩토리 별 테스트 결과는 구현 방식에 차이가 있을 뿐 결과는 동일하게 나옵니다.
- 추상 팩토리만 결과가 다르지만 그건 무기 코드가 붙어있으므로.. 다를 수 밖에 없네용
추상 팩토리가 쓰임새가 어디쓰일 지 아직 잘 생각이 안납니다.
제가 쓰게 된다면 Simple Factory 나 Factory Method를 주로 사용할 것 같은데
아직 좋은 점은 잘 못느끼겠습니다.. 더 알게 되고 지식이 넓어진다면 나중에 구조를 설계할 때 생각의 폭이 달라지겠지 싶습니다.