[Design Pattern] 21. Abstract Factory Pattern(추상 팩토리 패턴) C#
어떠한 개체의 관련 객체들의 모음을 생성할 수 있도록 하는 디자인 패턴입니다.
생성 패턴(Creational Design Patterns) 중 하나입니다.
장점
- 팩토리에서 생성되는 제품들의 상호 호환을 보장할 수 있습니다.
- 생성되는 클래스들과 기존 코드 사이의 단단한 결합을 피할 수 있습니다.
- 단일 책임 원칙을 만족하며 생성 코드를 한 곳으로 모아 코드를 더 쉽게 유지보수할 수 있습니다.
- 개방/폐쇄 원칙을 만족하여 기존 코드를 변경하지 않고 새로운 객체들을 생성할 수 있습니다.
단점
- 패턴을 적용하기 위해 인터페이스들과 클래스들이 많이 추가되기 때문에 코드가 많이 복잡해질 수 있습니다.
C#으로 Console 프로젝트를 하나 생성해서 할 것입니다.
이번엔 Pizza Factory에서 시카고 피자랑 뉴욕피자를 생성하겠습니다.
일단 프로젝트의 tree구조는 아래와 같습니다.
AbstractFactory(Project)
|- Program.cs (Main)
|- ICrust.cs (Abstract Product)
|- ThinCrust.cs (Concrete Product)
|- DeepDishCrust.cs (Concrete Product)
|- IPizza.cs (Abstract Product)
|- NewYorkPizza.cs (Concrete Product)
|- ChicagoPizza.cs (Concrete Product)
|- IPizzaFactory.cs (Abstract Factory interface)
|- NewYorkPizzaFactory.cs (Concrete Factory)
|- ChicagoPizzaFactory.cs (Concrete Factory)
ICrust.cs
namespace AbstractFactory
{
public interface ICrust
{
void AddSauce();
}
}
AddSauce를 구성해줍니다.
ThinCrust.cs
using System;
namespace AbstractFactory
{
public class ThinCrust : ICrust
{
public void AddSauce()
{
Console.WriteLine("[Thin Pizza] Add sauce");
}
}
}
ThinPizza에 소스 추가!
DeepDishCrust.cs
using System;
namespace AbstractFactory
{
public class DeepDishCrust : ICrust
{
public void AddSauce()
{
Console.WriteLine("[Deep Dish Pizza] Add a lot of sauce");
}
}
}
DeepDishPizza는 잔뜩 추가해 줍니다!
IPizza.cs
namespace AbstractFactory
{
public interface IPizza
{
void AddToppings(params string[] toppings);
}
}
Pizza에 토핑을 원하는 만큼 추가 할 수 있도록 params로 받아 줍니다.
* params는 가변인자 배열로 개수에 제한 없이 입력받게 해주는 키워드입니다.
NewYorkPizza.cs
using System;
namespace AbstractFactory
{
public class NewYorkPizza : IPizza
{
public void AddToppings(params string[] toppings)
{
Console.WriteLine($"[NewYork Pizza] Add {string.Join(", ", toppings)}");
}
}
}
구별하기 쉽도록 맨앞에 NewYork Pizza라고 적어주고 입력 받은 매개변수는 Join을 이용해 나열해 줍니다.
ChicagoPizza.cs
using System;
namespace AbstractFactory
{
public class ChicagoPizza : IPizza
{
public void AddToppings(params string[] toppings)
{
Console.WriteLine($"[Chicago Pizza] Add {string.Join(", ", toppings)}");
}
}
}
이름만 Chicago로 바꿔 뉴욕피자와 동일하게 구현해줍니다.
IPizzaFactory.cs
namespace AbstractFactory
{
public interface IPizzaFactory
{
IPizza CreatePizza();
ICrust CreateCrust();
}
}
피자를 생성하는 공장의 틀을 잡아줍니다. 크러스트와 피자를 만들게 구현해 주면 되겠죠?
NewYorkPizzaFactory.cs
namespace AbstractFactory
{
public class NewYorkPizzaFactory : IPizzaFactory
{
public IPizza CreatePizza()
{
return new NewYorkPizza();
}
public ICrust CreateCrust()
{
return new ThinCrust();
}
}
}
뉴욕 피자 공장입니다. 뉴욕 피자를 생성해 반환해주고 Thin Crust로 만들어 줍니다.
ChicagoPizzaFactory.cs
namespace AbstractFactory
{
public class ChicagoPizzaFactory : IPizzaFactory
{
public IPizza CreatePizza()
{
return new ChicagoPizza();
}
public ICrust CreateCrust()
{
return new DeepDishCrust();
}
}
}
시카고 피자 공장에서는 DeepDish 크러스트로 시카고 피자를 만들어 줍니다.
Program.cs
using System;
namespace AbstractFactory
{
class Program
{
static void Main(string[] args)
{
IPizzaFactory newYorkFactory = new NewYorkPizzaFactory();
IPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
IPizza newYorkPizza = newYorkFactory.CreatePizza();
ICrust newYorkCrust = newYorkFactory.CreateCrust();
IPizza chicagoPizza = chicagoFactory.CreatePizza();
ICrust chicagoCrust = chicagoFactory.CreateCrust();
newYorkCrust.AddSauce();
newYorkPizza.AddToppings("pepperoni");
Console.WriteLine();
chicagoCrust.AddSauce();
chicagoPizza.AddToppings("pepperoni", "minced meat");
}
}
}
뉴욕 피자 공장과 시카고 피자 공장을 만들고 각 피자를 생성해 줍니다.
그이후에 소스를 뿌리고 토핑을 추가해주면 완성입니다.
아래의 결과를 보시면 원하는 대로 나왔음을 확인 하실 수 있습니다.
이 패턴은 유용합니다만 상당히 많은 양의 코드를 요구해서 코드가 복잡해 지지않도록 추가하시는게 좋겠습니다.