추상 팩토리 패턴
상세화된 서브클래스를 정의하지 않고도 서로 관련성이 있거나 독립적인 여러 객체들을 한번에 생성하는 인터페이스를 제공하는 패턴이다.
구조
- AbstractFactory - 개념적 제품에 대한 객체를 생성하는 연산으로, 인터페이스를 정의한다.
- ContreteFactory - 구체적인 제품에 대한 객체를 생성하는 연산은 구현한다.
- AbstractProduct - 개념적 제품에 대한 인터페이스를 정의한다.
- ConctreteProduct - 구체적으로 팩토리가 생성한 객체를 정의하고, AbstractProduct가 정의하는 인터페이스를 구현한다.
- Client - AbstractFactory와 AbstractProduct 클래스에 선언된 인터페이스를 사용한다.
기본 인터페이스
namespace AbstractFactory;
//추상팩토리 인터페이스
public interface AbstractFactory
{
public TV CreateTV();
public Radio CreateRadio();
public Computer CreateComputer();
}
//구체 구현 서브클래스
public class LGFactory : AbstractFactory
{
public TV CreateTV()
{
Console.WriteLine("LG TV 생성");
return new LGTV();
}
public Radio CreateRadio()
{
Console.WriteLine("LG Radio 생성");
return new LGRadio();
}
public Computer CreateComputer()
{
Console.WriteLine("LG Computer 생성");
return new LGComputer();
}
}
public class SamsungFactory : AbstractFactory
{
public TV CreateTV()
{
Console.WriteLine("Samsung TV 생성");
return new SamsumTV();
}
public Radio CreateRadio()
{
Console.WriteLine("Samsung Radio 생성");
return new SamsungRadio();
}
public Computer CreateComputer()
{
Console.WriteLine("Samsung Computer 생성");
return new SamsungComputer();
}
}
//추상 제품 객체 인터페이스
public interface TV
{
}
//제품 구현 서브클래스
public class LGTV : TV
{
}
public class SamsumTV : TV
{
}
/*...*/
public interface Radio
{
}
/*...*/
public interface Computer
{
}
public class MainTest
{
public static int Main(string[] args)
{
AbstractFactory LGFactory = new LGFactory();
AbstractFactory SamsungFactory = new SamsungFactory();
//LG 제품군 생성
TV lgTV = LGFactory.CreateTV();
Radio lgRadio = LGFactory.CreateRadio();
Computer lgComputer = LGFactory.CreateComputer();
//Samsung 제품군 생성
TV ssTV = SamsungFactory.CreateTV();
Radio ssRadio = SamsungFactory.CreateRadio();
Computer ssComputer = SamsungFactory.CreateComputer();
return 0;
}
}
LG TV 생성
LG Radio 생성
LG Computer 생성
Samsung TV 생성
Samsung Radio 생성
Samsung Computer 생성
왜 사용해야 할까?
클라이언트에서 요구하는 제품군의 일관성을 유지하기 위해 사용한다.
우리가 자주 사용하는 운영체제인 윈도우와 맥을 예시로 생각해보자. 맥과 윈도우는 운영체제라는 관계를 공유하지만, 인터페이스와 단축키, 버튼 동작까지 많은 기능과 시스템에 큰 차이가 있다. 만약 클라이언트에서 윈도우에서 맥으로 운영체제 변경을 요구했는데, 기능 중 일부가 맥의 기능들로 변경되지 않고 윈도우의 기능으로 유지된다면 어떻게 될까? 기본 스타일의 차이가 발생하는 건 물론이고 기능의 오류가 발생할 가능성이 매우 높을 것이다.
//처음에는 윈도우 스타일을 사용한다.
System system = new WindowSystem();
Button button = new WindowButton();
ScrollBar scrollbar = new WindowScrollBar();
Interface interface = new WindowInterface();
//나중에 윈도우에서 맥으로 운영체제를 변경한다면, 위 구성품들을 모두 맥의 것으로 교체해야 한다.
system = new MacSystem();
button = new MacButton();
ScrollBar scrollbar = new WindowScrollBar(); // 실수로 윈도우 제품군으로 유지됨.
Interface interface = new macInterface();
추상 팩토리는 동일한 제품군들을 생성하는 인터페이스를 정의해놓고, 필요에 따라 제품군들을 쉽게 대체할 수 있기 때문에 오류를 최소화할 수 있다.
OprFactory factory = new MacFactory();
Button button = factory.createButton();
ScrollBar scrollbar = factory.createScrollbar();
Interface interface = factory.createInterface();
//윈도우 제품군을 생산하는 팩토리로 변경하면 된다.
factory = new WindowFactory();
Button button = factory.createButton();
ScrollBar scrollbar = factory.createScrollbar();
Interface interface = factory.createInterface();
장점
- 객체 생성에 필요한 코드들을 클래스화함으로써 코드 재사용성을 향상시킨다.
- 객체의 유형과 종속성을 캡슐화 함으로써 느슨한 결합을 이룸 - 수정이 발생하더라도 클라이언트 코드에 최소한의 영향을 끼침
- 제품군을 쉽게 대체할 수 있다.
- 제품 사이의 일관성을 증진시킨다.
단점
- 다수의 클래스를 생성해야 한다.
- 새로운 종류의 제품을 제공하기 위해서 구현 코드의 수정이 불가피하다.
- 만약 Mouse 제품을 추가로 생성하기 위해선 팩토리 인터페이스와 팩토리 구현 클래스에서 CreateMouse()를 추가하는 등의 수정을 해야한다.
추가 예시
스타크래프트에서 생산건물 별로 생산할 수 있는 유닛이 정해져 있다.
- 배럭 : 마린, 파이어뱃, 메딕, 고스트
- 게이트웨이 : 질럿, 드라군, 하이템플러, 다크템플러
추상 팩토리를 이용하면 각 생산건물을 추상팩토리 구현 클래스로 정의할 수 있다.
public interface AbstractBuilding
{
public Unit CreateUnitA();
public Unit CreateUnitB();
}
public class Barracks : AbstractBuilding
{
public Unit CreateUnitA(){ return new Marine(); }
public Unit CreateUnitB(){ return new Firebat(); }
}
public class Gateway : AbstractBuilding
{
public Unit CreateUnitA(){ return new Zealot(); }
public Unit CreateUnitB(){ return new Dragoon(); }
}
public interface Unit
{
public void Attack();
public void Move();
}
public class Marine : Unit
{
public void Attack(){ Console.WriteLine("마린 공격!"); }
public void Move(){ Console.WriteLine("마린 이동!"); }
}
public class Firebat : Unit
{
public void Attack(){ Console.WriteLine("파이어뱃 공격!"); }
public void Move(){ Console.WriteLine("파이어뱃 이동!"); }
}
public class Zealot : Unit
{
public void Attack(){ Console.WriteLine("질럿 공격!"); }
public void Move(){ Console.WriteLine("질럿 이동!"); }
}
public class Dragoon : Unit
{
public void Attack(){ Console.WriteLine("드라군 공격!"); }
public void Move(){ Console.WriteLine("드라군 이동!"); }
}
public class Starcraft
{
public static int Main(string[] args)
{
List<Unit> unitList = new List<Unit>();
//생산 기지 선택
AbstractBuilding building = new Barracks();
unitList.Add(building.CreateUnitA());
unitList.Add(building.CreateUnitB());
//생산 기지 변경
building = new Gateway();
unitList.Add(building.CreateUnitA());
unitList.Add(building.CreateUnitB());
foreach (var unit in unitList)
{
unit.Attack();
unit.Move();
}
return 0;
}
}
마린 공격!
마린 이동!
파이어뱃 공격!
파이어뱃 이동!
질럿 공격!
질럿 이동!
드라군 공격!
드라군 이동!
참고자료
'프로그래밍 이론 > 디자인 패턴' 카테고리의 다른 글
05. [Design Pattern] 싱글톤(Singleton) 패턴 (0) | 2023.11.16 |
---|---|
04. [Design Pattern] 프로토타입(Prototype) 패턴 (0) | 2023.11.07 |
03. [Design Pattern] 빌더(Builder) 패턴 (0) | 2023.11.07 |
01. [Design Pattern] 팩토리 메서드(Factory Method) 패턴 (0) | 2023.10.27 |
00. [Design Pattern] 디자인 패턴이란? (0) | 2023.10.25 |