Abstract Factory Pattern

Seungjae·2022년 4월 12일
0

디자인 패턴 공부

목록 보기
2/4

구체적인 클래스를 지정하지 않고 관련 객체의 패밀리를 생성할 수 있는 생성 디자인 패턴입니다.

Problem


가구 매장 시뮬레이터를 만들고 있다고 가정해보자. 고객의 요구사항은 어울리는 가구 제품군들을 받는 것이다. 동일한 패밀리의 다른 객체와 일치하도록 개별 가구 객체를 작성하는 방법이 필요하다. 또한 프로그램에 새 제품이나 제품군을 추가할 때 기존 코드를 변경하고 싶지 않다. 가구 공급업체는 카탈로그를 매우 자주 업데이트하므로 발생할 때마다 핵심 코드를 변경하고 싶지 않을 것이다.

Solve


Abstract Factory 패턴이 제안하는 첫 번째는 제품군의 개별 제품에 대한 인터페이스를 명시적으로 선언하는 것이다. 그런 다음 제품의 모든 변형이 이러한 인터페이스를 따르도록 한다. 예를 들어, 모든 의자 변형은 Chair 인터페이스 를 구현할 수 있다.

다음으로 제품 패밀리 내 모든 제품에 대한 생성 방법 목록이 있는 인터페이스인 추상 팩토리를 선언한다. 이러한 메서드는 각각 알맞은 추상 제품 유형을 반환해야 한다.

제품군의 각 변형에 대해 AbstractFactory 인터페이스를 기반으로 별도의 팩토리 클래스를 생성한다. 팩토리는 특정 종류의 제품을 반환하는 클래스이다. 클라이언트 코드는 각각의 추상 인터페이스를 통해 공장과 제품 모두에 대해 작동한다. 이를 통해 실제 클라이언트 코드를 손상시키지 않고 클라이언트 코드에 전달하는 팩토리 유형과 클라이언트 코드가 수신하는 제품 변형을 변경할 수 있게 된다.

고객이 공장에서 의자를 생산하기를 원한다고 가정해보자. 클라이언트는 공장의 구체 클래스를 알 필요도 없고 어떤 종류의 의자를 가져오는지도 알 필요없다. 현대 모델이든 빅토리아 스타일의 의자이든 클라이언트는 추상 Chair인터페이스를 사용하여 모든 의자를 동일한 방식으로 취급해야 한다. 또한 어떤 변형의 의자가 반환되든 동일한 공장 개체에서 생산된 테이블의 유형과 항상 일치한다.

명확히 해야 할 것이 한 가지 더 있다. 클라이언트가 추상 인터페이스에만 노출된다면 실제 팩토리 객체를 생성하는 것은 무엇일까? 일반적으로 애플리케이션은 초기화 단계에서 구체적인 팩토리 객체를 생성한다. 그 직전에 앱은 구성 또는 환경 설정에 따라 공장 유형을 선택해야하고 그러는 것이 일반적이다.

Code


FurnitureFactory

package abstract_factory

interface FurnitureFactory {
  fun createChair(): Chair
  fun createTable(): Table
}

ModernFactory

package abstract_factory

class ModernFactory : FurnitureFactory {
  override fun createChair() = ModernChair()

  override fun createTable() = ModernTable()
}

VictorianFactory

package abstract_factory

class VictorianFactory: FurnitureFactory {
  override fun createChair() = VictorianChair()

  override fun createTable() = VictorianTable()
}

Chair

package abstract_factory

interface Chair {
  fun print()
}

ModernChair

package abstract_factory

class ModernChair: Chair {
  override fun print() {
    println("It's a modern chair")
  }
}

VictorianChair

package abstract_factory

class VictorianChair: Chair {
  override fun print() {
    println("It's a victorian chair")
  }
}

Table

package abstract_factory

interface Table {
  fun print()
}

ModernTable

package abstract_factory

class ModernTable: Table {
  override fun print() {
    println("It's a modern table")
  }
}

VictorianTable

package abstract_factory

class VictorianTable: Table {
  override fun print() {
    println("It's a victorian table")
  }
}

Application

package abstract_factory

class Application(private val factory: FurnitureFactory) {

  fun printMySet() {
    factory.createChair().print()
    factory.createTable().print()
  }
}

fun main() {
  println("What type of furniture do you like?")
  println("1. Victorian")
  println("2. Modern")

  val factory: FurnitureFactory = when (readLine()) {
    "1" -> VictorianFactory()
    "2" -> ModernFactory()
    else -> {
      println("Error!")
      return
    }
  }

  val application = Application(factory)
  application.printMySet()
}

구조


적용가능성


  • 코드가 관련 제품의 특정한 제품 패밀리와 함께 작동해야 하지만 해당 제품의 구체적인 클래스에 의존하고 싶지 않은 경우
  • Abstract Factory는 각 구체 클래스에서 객체를 생성하기 위한 인터페이스를 제공한다. 코드가 이 인터페이스를 통해 객체를 생성하는 한 앱에서 이미 생성한 제품과 일치하지 않는 잘못된 제품 변형을 생성하는 것에 대해 걱정할 필요가 없다.
  • 기본 책임을 흐리게 하는 팩토리 메소드 세트가 있는 클래스가 있는 경우 추상 팩토리 구현을 고려

장점﹒단점


장점

  • 공장에서 생성한 제품이 서로 호환되는 것을 보장한다.
  • 구체적인 제품과 클라이언트 코드 간의 긴밀한 결합을 피한다.
  • SRP => 제품 생성 코드를 한 곳으로 추출
  • OCP => 기존 클라이언트 코드를 손상시키지 않고 제품의 새로운 변형을 도입 가능

단점

  • 많은 새로운 인터페이스와 클래스가 패턴과 함께 도입되기 때문에 코드가 생각보다 복잡해질 수 있다.

Git


https://github.com/oh980225/DesignPattern/tree/main/src/main/kotlin/abstract_factory

Ref


https://refactoring.guru/design-patterns/abstract-factory

profile
코드 품질의 중요성을 아는 개발자 👋🏻

0개의 댓글