프로그래밍 이론/디자인 패턴

02. [Design Pattern] 추상 팩토리(Abstract Factory) 패턴

NewtronVania 2023. 10. 27. 22:47

추상 팩토리 패턴

상세화된 서브클래스를 정의하지 않고도 서로 관련성이 있거나 독립적인 여러 객체들을 한번에 생성하는 인터페이스를 제공하는 패턴이다.


구조

  • 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();

장점

  1. 객체 생성에 필요한 코드들을 클래스화함으로써 코드 재사용성을 향상시킨다.
  2. 객체의 유형과 종속성을 캡슐화 함으로써 느슨한 결합을 이룸 - 수정이 발생하더라도 클라이언트 코드에 최소한의 영향을 끼침
  3. 제품군을 쉽게 대체할 수 있다.
  4. 제품 사이의 일관성을 증진시킨다.

단점

  1. 다수의 클래스를 생성해야 한다.
  2. 새로운 종류의 제품을 제공하기 위해서 구현 코드의 수정이 불가피하다.
    • 만약 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;
    }
}
마린 공격!
마린 이동!
파이어뱃 공격!
파이어뱃 이동!
질럿 공격!
질럿 이동!
드라군 공격!
드라군 이동!

참고자료

 

GoF의 디자인 패턴 - 예스24

이 책은 디자인 패턴을 다룬 이론서로 디자인 패턴의 기초적이고 전반적인 내용을 학습할 수 있다.

www.yes24.com