설계패턴(Design Pattern)

[디자인 패턴] 4.(2) 추상 팩토리 패턴 (Abstract Factory Pattern)

긍.응.성 2024. 6. 16. 14:34
반응형

이 글은 헤드퍼스트 디자인패턴과 GoF 디자인패턴을 읽고 정리한 글입니다.

1. 추상 팩토리 패턴(Abstract Factory Pattern)

구상 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체로 이루어진 제품군(families)을 생산하는 인터페이스를 제공합니다. 구상 제품 클래스는 서브클래스에서 생성합니다.

1.1. 구성 요소

AbstractFactory

  • Product 생성을 위한 Factory 인터페이스로 AbstractFactory라 합니다.
  • AbstractProduct 타입을 생성 후 반환하는 메서드를 갖습니다.

ConcreteFactory

  • AbstractFactory를 구현한 구상 팩토리 클래스입니다.
  • 실제 ConcreteProduct를 생성하여 반환하는 메서드를 구현합니다.

AbstractProduct

  • 생성될 Product의 인터페이스 타입입니다.
  • Product 제품군을 구성하는 개별 제품에 대한 인터페이스입니다.

ConcreteProduct

  • AbstractProduct를 구현하는 변형 제품에 대한 구상클래스입니다.
  • ConcreteFactory를 통해 생성됩니다.

Client

  • Client는 AbstractFactory, AbstractProduct의 인터페이스 만을 사용하게됩니다.
  • ConcreteFactory, ConcreteProduct에 대하여 알 필요가 없습니다.

1.2. 적용 방법

  1. Product에 대하여 고유한 종류(type)와 변형(variants)에 매핑된 테이블을 만듭니다. 예를들어 Product 종류를 의자와 책상이라고 하면 변형은 철제(의자/책상), 나무(의자/책상), 돌(책상/의자)이 있을수 있습니다.
  2. 모든 Product 종류에 대하여 AbstractProduct로 등록합니다. 그리고 모든 ConcreteProduct가 AbstractProduct들을 구현하도록 합니다.
  3. AbstractFactory를 선언하고 모든 AbstractProduct를 생성하는 메서드를 선언합니다.
  4. 각 제품 변형에 대하여 ConcreteFactory를 선언하고 ConcreteProduct을 생성하여 반환하는 구현을 채워넣습니다.
  5. 팩토리 초기화 코드를 생성하고 모든 ConcreteFactory를 직접 생성하던(new) 코드 부분을 팩토리를 사용하도록 교체합니다.

1.3. 적용 시기

  • 시스템을 Product를 생성하는 로직, 구성 요소, 구현 클래스 타입으로 부터 독립적이고 싶을 때 사용하면 좋습니다.
  • 하나의 Product가 아닌 다양한 제품군(families) 단위로 동작하여야 할때 추상 팩토리 패턴을 사용하기 좋습니다. 단순히 한번 Factory를 교체하는것으로 생성되는 모든 제품군에 대하여 수정할 수 있습니다.

1.4. 정리

추상 팩토리 패턴의 가장 큰 장점은 제품군에 대한 교체를 쉽게 할 수 있다는 점입니다. 변형 제품에 대하여 ConcreteFactory를 정의하고 이를 교체하여 사용하는 것 만으로 생성하는 제품군을 교체할 수 있습니다. 또한, 제품군 단위로 교체가 가능하다는 점은 일관성을 높입니다. 제품군 단위 교체는 개별 제품 단위로 생성 로직을 교체하는 것에 비하여 더 빠르고 안전하게 처리할 수 있습니다.

추상 팩토리는 객체를 생성하는 작업을 팩토리에 위임하고 이를 추상 팩토리로, 그리고 제품에 대해서는 공통 요소를 제품 인터페이스로 분리합니다. 클라이언트는 구현 클래스가 아닌 인터페이스 수준에서 소통하게 되는데 이는 코드의 유연함으로 이어집니다.

하지만, 추상 팩토리 패턴은 단점도 존재합니다. 만약 새로운 종류의 Product가 추가된다면 추상 팩토리에 해당 제품을 생성하는 추가 메서드가 생성되어야합니다. 그리고 이를 구현하는 모든 ConcreteFactory에서 새로운 Product를 생성하는 코드가 추가되어야 합니다.

2. 추상 팩토리 패턴 구현 방법

2.1. 팩토리들을 싱글턴으로 생성하라

일반적으로 애플리케이션은 제품군 단위로 하나의 ConcreteFactory만 필요합니다. 그렇기에 이 팩토리는 싱글턴으로 생성하여 여러 코드에서 이를 참조하여 사용하도록 하는것으르 권장합니다.

2.2. Product 생성하기

AbstractFactory는 Product를 만들기 위한 인터페이스를 선언할 뿐입니다. 실제로 Product를 생성 구현 방법으로 팩토리 메서드를 사용하는 방법 프록시 패턴을 제시합니다.

먼저 팩토리 메서드 패턴을 이용하는 방법입니다(팩토리 메서드 패턴에 대해 궁금하시다면 이 페이지를 확인해주세요). 각 Product에 대하여 팩토리 메서드(factoryMethod)를 정의합니다. 팩토리 메서드 패턴을 참고하여 Product의 생성을 위한 Creator 클래스를 생성하고 factoryMethod() 를 추가합니다. ConcreteFactory는 Creator 클래스 구현, factoryMethod를 오버라이드하여 제품을 생성할 수 있습니다. 이 방법의 아쉬운 점이라면 새로운 제품군에 대하여도 새로운 ConcreteFactory를 만들어 주어야한다는 것입니다.

읽은대로 해석한 팩토리 메서드를 사용한 구현 방법인데 혹시 틀렸다면 댓글로 지적 부탁드립니다 (_ _)

만약 제공되어야할 제품군이 많다면 프록시 패턴을 사용하여 ConcreteFactory를 구현하는 방법이 있습니다. ConcreteFactory 초기화 시 각 제품군 내 Product는 프로토타입이 가능한 인스턴스로 초기화합니다. 그리고 생성 요청 시 인스턴스를 단순히 클로닝(clone)하여 새로운 Product 인스턴스 생성하는 방법입니다. 추상 메서드 패턴을 프로토타입 패턴과 연계한다면 추가되는 제품군에 대해서도 새로운 ConcreteFactory를 만들어주지 않아도 됩니다.

프로토타입 패턴과 연계한 추상 팩토리 패턴의 구현 예시는 이 링크에서 확인하실 수 있습니다.

2.3. 확장가능한 팩토리로 정의하라

추상 팩토리 패턴의 단점은 Product가 추가될 때 변경사항이 많다는 점입니다. 이러한 변경에 대하여 닫혀있는 확장성있는 팩토리를 만드는 방법이 있습니다. 이는 팩토리에 생성할 Product에 대해 파라미터를 받는 메서드를 제공하는 것입니다.

예를 들어 추상 팩토리는 generate(productId) 메서드를 제공하고 ConcreteFactory에서 productId 를 통해 이에 맞는 Product를 생성하도록 구현이 되어있다고 합시다. 새로운 Product가 추가된 경우 동작에서 바뀌는 점은 새로운 productId를 파라미터로 전달하는 것입니다. AbstractFactory는 새로운 Product를 위한 메서드가 추가되어야할 필요가 없으며, ConcreteFactory에서 새로운 productId에 대한 생성만 지원하면 됩니다.

하지만 위 방법으로 새롭게 추가되는 문제가 있습니다. 이는 generate 메서드가 하나의 반환 타입을 갖기 때문인데요, 이 방법을 사용한다면 타입 캐스팅이 불가피할것으로 보입니다.

3. 참고 링크

반응형