[Design Pattern] 팩토리 패턴(Factory Pattern)

2024. 12. 9. 18:38STUDY/디자인 패턴

728x90
반응형

이 글은 'level up your code with game programming patterns' 서적을 기반으로 각종 블로그에서 추가적인 정보를 얻어 작성하였습니다.

객체 지향 디자인 패턴의 기본 원칙은 확장에 있어서는 열려 있어야 하며, 수정에 있어서는 닫혀 있어야 한다는 것이다.(OCP, Open Closed Principle)

여기서 수정에 있어서 닫혀 있어야 한다는 말에 주목 해보자. 코드를 수정하지 않아도 모듈의 기능을 확장하거나 변경 할 수 있어야 한다. 때문에 수정이 일어날 가능성이 큰 부분과 그렇지 않은 부분을 분리하는 것이 좋다. 객체는 속성과 함수가 변경 또는 추가될 수 있다. 때문에 객체의 생성을 담당하는 코드는 변경의 가능성이 높다. 객체의 생성을 담당하는 클래스를 한 곳에서 관리하여 결합도를 줄이기 위하여 팩토리 패턴이 나타나게 된 것이다. 

[Factory Pattern]

단순한 팩토리 패턴으로 Product를 만드는 하나의 예시다.

public class Program
{
    public Factory _factory = new Factory();
    public void Main(string[] args)
    {
        _factory.CreateProduct("A");
    }
}

public class Factory
{
    public Product CreateProduct(string type)
    {
        if(type == "A")
            return new ProductA();
        else if(type == "B")
            return new ProductB();
        else 
            return new ProductC();
    }
}

 

제품을 생성하는 부분은 제품이 추가되는 등의 수정이 빈번하게 일어날 수 있기 때문에 새로운 Factory 클래스로 만들었다. 이러면 CreateProduct() 함수를 호출하는 부분에서는 어떻게 생성되는지는 더 이상 신경쓸 필요없으며 수정은 이 안에서만 일어나게 된다.

한 단계 더 멋진 방법을 사용해보자.

만약 공장을 서브 클래스화하여 특정 제품만 제조하는 여러 개의 공장을 만든다고 가정해보자. 그렇게 된다면 적이나 장애물을 비롯한 다양한 항목을 생성하는데 도움이 된다. 게임 레벨에서 아이템을 인스턴스화하는 팩토리 패턴을 만든다고 가정해보자. 프리팹을 사용해 게임 오브젝트를 만든 한편, 인스턴스를 생성할 때마다 커스텀 동작을 실행하고 싶을 수 있다.

if문 또는 switch문을 사용하여 이 로직을 유지하는 대신, IProduct라는 인터페이스를 만들고 Factory라는 추상 클래스를 만들어 관리해보자.

 

[Factory Method Pattern]

팩토리 메서드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정하게 만드는 패턴이다.

public interface IProduct
{
    public string ProductName { get; set; }
    public void Init();
}

public class ProductA : IProduct
{
    private string _productName = "ProductA";
    public string ProductName { get => _productName; set => _productName = value; }
    public void Init() { }
}

public class ProductB : IProduct
{
    private string _productName = "ProductB";
    public string ProductName { get => _productName; set => _productName = value; }
    public void Init() { }    
}

public abstract class Factory
{
    public abstract IProduct GetProduct();
}

public class ConcreteFactoryA : Factory
{
    public ProductA product = new ProductA();

    public override IProduct GetProduct()
    {
        var newProduct = new ProductA();
        newProduct.Init();
        return newProduct;
    }
}

public class ConcreteFactoryB : Factory
{
    public override IProduct GetProduct()
    {
        var newProduct = new ProductB();
        newProduct.Init();
        return newProduct;
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var productA = new ConcreteFactoryA().GetProduct();
        var productB = new ConcreteFactoryB().GetProduct();
    }
}

 

기본 클래스인 Factory에는 IProduct를 반환하는 GetProduct 메서드가 있다. 추상 메서드이므로 Factory 인스턴스를직접만들 수는 없다. 두어 가지의 구상 서브 클래스(ConcreteFactoryA 및 ConcreteFactoryB)를 파생시키면 실질적으로 다른 제품을 얻게 된다. 

장점과 단점

팩토리 패턴은 많은 제품을 설정할때 가장 유용하다. 새로운 제품 유형을 새로 정의하더라도 기존 유형이 변경되지 않으며, 이전 코드를 수정할 필요가 없다. 

각 제품의 내부 로직을 제품 자체 클래스 내부에 두면 공장 코드를 비교적 짧게 유지할 수 있다. 각 공장은 제품별로 Init 메서드를 호출하는 방법만 알고 기본적인 세부 사항은 알지 못한다.

하지만 이때 패턴을 구현하려면 다수의 클래스와 서브 클래스를 만들어야 한다는 단점이 있다. 다른 패턴과 마찬가지로 이로 인해 약간의 오버헤드가 발생하며 이는 매우 다양한 제품이 필요하지 않다면 없어도 되는 오버헤드다.

 

[Abstract Factory Pattern]

추상 팩토리 패턴은 많은 수의 연관된 서브 클래스를 특정 그룹으로 묶어 한번에 교체할 수 있도록 만든 디자인 패턴이다.

추상 팩토리 패턴은 팩토리 메서드 패턴과 유사하다. 하지만 한가지 다른 것은 팩토리를 만드는 상위 팩토리 클래스가 존재한다. 또한 복수의 하위 클래스(Factory)를 가지게 된다.

팩토리 vs 팩토리 메서드 vs 추상 팩토리 비교

Factory Method Pattern vs Abstract Factory Pattern

추상 팩토리 패턴은 팩토리 메서드 패턴의 확장이라고 볼 수 있지만, 더 좋은 패턴이라고 단정 지을 수는 없다. 추상 팩토리 패턴은 동일한 처리로직의 하위클래스의 결합을 통해 선택적으로 객체를 생성할 수 있다는 장점이 있지만, 새로운 객체가 추가된다면 하위 클래스도 같이 추가하게 되어, 확장시 모든 하위 클래스의 수정이 필요할 수 도 있다.

따라서 본인이 진행하고 있는 프로젝트의 규모와 설계에 따라 적절한 패턴을 사용하는 것이 좋다.

 

 

728x90
반응형

'STUDY > 디자인 패턴' 카테고리의 다른 글

[Design Pattern] 커맨드 패턴(Command pattern)  (1) 2024.12.09
[Design Pattern] MVC 패턴과 MVP 패턴  (1) 2024.12.09
SOLID 원칙  (0) 2024.12.08
Static, 싱글톤  (0) 2022.07.01