추상 팩토리 패턴 ( Design Pattern )
2022.09.19
프로그래밍 디자인 패턴을 공부했습니다.
먼저 기본 개념을 다이어그램으로 정리 해보았습니다.
- 추상적인 팩토리를 구성한다.
- 구성한 팩토리를 받아오는 팩토리 클래스
- 클라이언트에서 가상화된 요소들로 ProductA 와 ProductB를 가진다.
- 상황 or 조건에 맞는 구체적인 요소들을 구현한다.

먼저 만들어야 할 컴포넌트들을 추상적으로 정하고, 구체적인 상황이 주어지면 컴포넌트들을 상황에 맞게 구체적으로 구현하는 패턴이라고 볼 수 있습니다. 각 종류별로 Factory를 만들어 내는 것 보다, 관련있는 객체들끼리 묶어서 사용합니다.
팩토리 메서드 패턴과 비슷하지만, 객체 하나하나를 반환하는 메서드 패턴과 달리 구조의집합 그 자체를 반환합니다.
보안성 부분에서 클래스의 내부 부분은 다른 빼서 인터페이스나 Abstract로만 접근 가능하게 한다.
국민게임 스타크래프트로 예시를 많이 참고하였습니다.
스타크래프트에는 3가지 종족이 있습니다. 각각 유닛도, 능력도, 이름도 다 다릅니다. 이와 같이 여러가지 건물과 유닛을 전부 따로 관리를 해야하는데 추상 팩토리 패턴을 이용하면 특정 그룹으로 묶어 관리 할 수 있습니다.
먼저 인구관리 건물을 추상클래스로 구현했습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class RaceCapacity
{
//인구 수 확장
public abstract void expend();
}
public class Pylon: RaceCapacity
{
public override void expend()
{
Debug.Log("프로토스 인구 확장 + 8");
}
}
public class SupplyDepot: RaceCapacity
{
public override void expend()
{
Debug.Log("테란 인구 확장 + 8");
}
}
public class Overload: RaceCapacity
{
public override void expend()
{
Debug.Log("저그 인구 확장 + 8");
}
}
expend()를 구현해야하며, 종족별 인구를 늘리는 건물의 기능을 함유하고 있습니다.
종족별로 프로토스 : 파일런, 테란 : 서플라이, 저그 : 오버로드(유닛) 이렇게 인구를 관리하는 점을 공통으로 묶어 구현했습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class UnitBuilding{
public abstract void Produce();
}
public class GateWay : UnitBuilding
{
public override void Produce()
{
Debug.Log("프로토스 유닛 생산");
}
}
public class barrucks : UnitBuilding
{
public override void Produce()
{
Debug.Log("테란 유닛 생산");
}
}
public class Lava : UnitBuilding
{
public override void Produce()
{
Debug.Log("저그 유닛 생산");
}
}
다음은 유닛을 생성하는 건물의 클래스 입니다. 구조는 위와 같습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Factory : MonoBehaviour
{
public enum Race
{
Terran,
Protoss,
Zerg
}
public abstract class RaceFactory
{
public abstract RaceCapacity makeCapacityBuilding();
public abstract UnitBuilding makeUnitBuilding();
}
public class TerranFactory : RaceFactory
{
public override RaceCapacity makeCapacityBuilding()
{
return new SupplyDepot();
}
public override UnitBuilding makeUnitBuilding()
{
return new Barrucks();
}
}
public class ProtossFactory : RaceFactory
{
public override RaceCapacity makeCapacityBuilding()
{
return new Pylon();
}
public override UnitBuilding makeUnitBuilding()
{
return new GateWay();
}
}
public class ZergFactory : RaceFactory
{
public override RaceCapacity makeCapacityBuilding()
{
return new Overload();
}
public override UnitBuilding makeUnitBuilding()
{
return new Lava();
}
}
}
종족별 건물을 모았습니다. 우리는 종족(세부 요소)를 몰라도 makeCapacityBuilding과 makeUnitBuilding을 호출하면 각각 맞는 역할을 수행할 것 입니다.
아래는 사용해 볼수 있는 에제 코드입니다.
using UnityEngine;
public class RaceAbstractFactory : MonoBehaviour
{
public Race type = Race.Terran;
public RaceFactory getFactory()
{
RaceFactory factory = null;
switch(type)
{
case Race.Terran:
factory = new TerranFactory();
break;
case Race.Protoss:
factory = new ProtossFactory();
break;
case Race.Zerg:
factory = new ZergFactory();
break;
}
return factory;
}
}
switch문으로 enum의 종족값을 받아와서 맞는 case에 넣고 실행하며 Protoss 인지 Terran인지 Zerg인지 확인하고 결과를 리턴 받으면 된다. 다음은 위의 코드를 사용하는 팩토리 코드입니다
sing UnityEngine;
public class FactoryUse : MonoBehaviour
{
void Start()
{
RaceAbstractFactory raceFactoryUse = new RaceAbstractFactory();
RaceFactory factory = raceFactoryUse.getFactory();
RaceCapacity capacity = factory.makeCapacityBuilding();
UnitBuilding building = factory.makeUnitBuilding();
capacity.expend();
building.produce();
}
}
getFactory로 받아온 종족값의 의한 factory의 makeCapacityBuilding 과 makeUnitBuilding에 접근하여 각각의 기능인 expend()와 produce()를 수행할수 있는 코드를 작성했습니다.
여기서 가장 중요한 점을 말하며 마무리 하자면, 사용하는 입장(client)에서 종족(세부요소)는 무엇인지 알 필요가 없습니다. 오로지 추상 팩토리의 기능을 수행하며 그에 맞는 세부 요소는 상황과 조건에 의해 충분히 바꿔 사용할 수 있기 때문입니다.
이렇게 여러 종류의 객체를 생성할 때 객체들 사이 규칙이나 관련송(조건, 특성 등) 으로 추상 팩토리 패턴을 적용하여 관리하면 편합니다.