1. 추상 팩토리 패턴이란?
- 관련성 있는 여러 종류의 객체를 일관된 방식으로 생성하는 경우 사용됨.
- 관련 객체들의 구상 클래스들을 지정하지 않고도 관력 객체들의 모음을 생성할 수 있도록 하는 생성패턴
- 연관성이 있는 객체 군이 여러개 있을 경우 이들을 묶어 추상화하고, 어떤 구체적인 상황이 주어지면 팩토리 객체에서 집합으로 묶은 객체 군을 구현화 하는 생성 패턴.
2. 팩토리 메서드 패턴과의 공통점과 차이점
- 먼저, 추상 팩토리 패턴이 팩토리 메서드 패턴의 상위 호환이 아니라는 것에 주의!
- 팩토리 메서드 패턴은 어떤 객체를 생성할 지에 집중하고, 추상 팩토리 패턴은 연관된 객체들을 모아둔다는 것에 집중함.
- 조건에 따라 객체 생성을 팩토리 클래스로 위임하여 팩토리 클래스에서 객체를 생성하는 팩토리 메서드 패턴과 달리, 추상 팩토리 패턴은 서로 관련이 있는 객체들을 통째로 묶어서 팩토리 클래스로 만들고, 이 팩토리를 조건에 따라 생성하도록 다시 팩토리를 만들어서 객체를 생성하는 패턴(팩토리 메서드를 좀 더 캡슐화한 방식)
팩토리 메서드 패턴 | 추상 팩토리 패턴 | |
공통점 | 1. 객체 생성 과정을 추상화한 인터페이스를 제공 2. 객체 생성을 캡슐화함으로써 구체적인 타입을 감추고 느슨한 결합 구조를 가짐 |
|
차이점 | 구체적인 객체 생성 과정을 하위 또는 구체적인 클래스로 옮기는 것이 목적 |
관련있는 여러 객체를 구체적인 클래스에 의존하지 않고 만들 수 있게 해주는 것이 목적 |
한 팩토리 당, 한 종류의 객체 생성 지원 |
한 팩토리에서, 서로 연관된 여러 종류의 객체 생성을 지원(제품군 생성 지원) |
|
메소드 레벨에서 포커스를 맞춰, 클라이언트의 ConcreateProduct 인스턴스의 생성 및 구성에 대한 의존을 감소 |
클래스 레벨에서 포커스를 맟춰, 클라이언트 ConcreateProduct 인스턴스 군의 생성 및 구성에 대한 의존을 감소 |
3. 추상 팩토리 패턴의 장점
1) 일관성 : 제품 군 집합을 타입 별로 일관된 방식으로 찍어낼 수 있다.
2) 확장성 : 관리와 기능 확장이 용이함.
3) 은닉성 : 구체적인 내용을 클라이언트에게 감출 수 있음
4) 낮은 결합도 : 구체적인 클래스 대신 인터페이스를 사용하기 때문에, 추상화된 팩토리 인터페이스에 의존하게 되어 클라이언트의 코드와 구체적인 클래스 사이의 결합도를 낮출 수 있음.
4. 추상 팩토리 패턴의 단점
1) 복잡성 : 객체들의 생성을 추상화하기 때문에 추가적인 추상화 계층을 도입하게 되어 시스템 구조가 복잡해지고 코드의 양이 증가할 수 있음.
2) 유연성 제한 : 미리 정의된 팩토리들을 사용하여 객체들을 생성하기 때문에 동적인 객체 생성에는 제한이 있다. 즉, 실행중에 팩토리를 변경하여 다른 종류의 객체를 생성하는 것을 어렵다.
5. 코드 구현(Unity, C#)
<예시>
- 고블린 생성 시스템
- 참고 블로그 : https://welcomeheesuk.tistory.com/66
1. 가장 기반이 되는 추상클래스 EqipmentFactory
// 슈퍼클래스
public abstract class EquipmentFactory : MonoBehaviour
{
// 몬스터 객체를 꾸며줄 추상 메소드들
public abstract Weapon CraeteWeapon();
public abstract Helmet CreateHelmet();
public abstract BodyCloth CreateBodyCloth();
}
2. 추상클래스를 상속받는 서브 클래스
// 고블린 몬스터를 꾸며주는 고블린의 장비 팩토리
public class GoblinEquipmentFactory : EquipmentFactory
{
// 장비 프리팹을 멤버변수로 가짐
[SerializeField]
public Weapon goblinWeapon = null;
[SerializeField]
public Helmet goblinHelmet = null;
[SerializeField]
public BodyCloth goblinBodyCloth = null;
// 추상 메소드를 상속받아 재정의
public override Weapon CreateWeapon
{
Weapon weapon = Instantiate(this.goblinWeapon).GetComponent<Weapon>();
return weapon;
}
public override Helmet CreateHelmet
{
Helmet helmet = Instantiate(this.goblinHelmet).GetComponent<Helmet>();
return helmet;
}
public override BodyCloth CreateBodyCloth
{
BodyCloth bodyCloth = Instantiate(this.goblinBodyCloth).GetComponent<BodyCloth>();
return bodyCloth;
}
}
3. 장비 멤버변수를 몬스터 클래스에 추가해줌
public abstract class Monster : MonoBehaviour
{
// 몬스터 클래스의 무기, 헬멧, 옷 멤버변수 추가
protected Weapon weapon = null;
protected Helmet helmet = null;
protected BodyCloth bodyCloth = null;
// 추상팩토리를 매개변수로 받는 장비를 세팅하는 메소드 추가
public abstract void SetEquipment(EquipmentFactory _equipmentFactory);
}
4. 고블린을 꾸며주기 위한 SetEquipment 메소드 구현
public class Goblin : Monster
{
// 장비 팩토리 클래스를 멤버변수로 가짐(재사용이 되지 않는다면 없어도 됨)
private EquipmentFactory goblinEquipmentFactory = null;
// 장비를 세팅해주는 추상 메소드를 재정의
public override void SetEquipment(EquipmentFactory _equipmentFactory)
{
this.goblinEquipmentFactory = _equipmentFactory;
this.weapon = this.goblinEquipmentFactory.CreateWeapon();
this.weapon = this.transform.SetParent(this.transform, false);
this.helmet = this.goblinEquipmentFactory.CreateHelmet();
this.helmet = this.transform.SetParent(this.transform, false);
this.bodyCloth = this.goblinEquipmentFactory.CreateBodyCloth();
this.bodyCloth = this.transform.SetParent(this.transform, false);
}
}
5. 실행부
public class GoblinMonsterFactory : MonsterFactory<MONSTER_GOBLIN>
{
// 고블린과 관련된 객체를 멤버변수로 가짐
[SerializeField]
private GameObject goblinPrefab = null;
[SerializeField]
private GameObject goblinBigPrefab = null;
[SerializeField]
private GameObject goblinKingPrefab = null;
[SerializeField]
private EquipmentFactory equipmentFactory = null;
// MonsterFactory에서 상속받은 create 함수를 재정의
protected override Monster Create(MONSTER_GOBLIN _type)
{
Goblin goblin = null;
switch(_type)
{
// 매개변수로 받은 Enum 변수에 따라 고블린 객체 생성
case MONSTER_GOBLIN.NORMAL :
goblin = Instantiate(this.goblinPrefab).GetComponent<Goblin>():
break;
case MONSTER_GOBLIN.BIG :
goblin = Instantiate(this.goblinBigPrefab).GetComponent<Goblin>():
break;
case MONSTER_GOBLIN.KING :
goblin = Instantiate(this.goblinKingPrefab).GetComponent<Goblin>():
break;
}
// 고블린이 생성된 이후 장비를 세팅하여 생성된 고블린 객체의 정의를 결정해줌
goblin.SetEquipment(this.equipmentFactory);
return goblin;
}
}