디자인 패턴

Abstarct Factory 패턴

Keisa 2024. 6. 13. 17:01

출처: https://refactoring.guru/ko

 

리팩터링과 디자인 패턴

Hello, world! Refactoring.Guru는 리팩토링, 디자인 패턴, SOLID 원칙 및 기타 스마트 프로그래밍 주제에 대해 알아야 할 모든 것을 쉽게 찾을 수 있는 자원입니다. 이 사이트에서는 이러한 모든 주제가 어

refactoring.guru

 

 

추상 팩토리는 관련 객체들의 구상 클래스들을 지정하지 않고도 관련 객체들의 모음을 생성할 수 있도록 하는 생성패턴입니다.

 문제

예를 들어 당신이 가구 판매장을 위한 프로그램을 만들고 있다고 가정합시다. 당신의 코드는 다음을 나타내는 클래스들로 구성됩니다:

  1. 관련 제품들로 형성된 패밀리​(제품군), 예: Chair​(의자) + Sofa​(소파) + Coffee­Table​(커피 테이블).
  2. 해당 제품군의 여러 가지 변형. 예를 들어 Chair​(의자) + Sofa​(소파) + Coffee­Table​(커피 테이블) 같은 제품들은 Modern​(현대식), Victorian​(빅토리안), Art­Deco​(아르데코 양식)​와 같은 변형으로 제공됩니다.

제품 패밀리들과 그들의 변형들

이제 당신이 새로운 개별 가구 객체를 생성했을 때, 이 객체들이 기존의 같은 패밀리 내에 있는 다른 가구 객체들과 일치하는 변형​(스타일)​을 가지도록 할 방법이 필요합니다. 그 이유는 당신의 고객이 스타일이 일치하지 않는 가구 세트를 받으면 크게 실망할 수 있기 때문입니다.

현대식 스타일의 소파는 빅토리안 스타일의 의자들과 잘 어울리지 않습니다.

또, 가구 공급업체들은 카탈로그를 매우 자주 변경하기 때문에, 그들은 새로운 제품 또는 제품군​(패밀리)​을 추가할 때마다 기존 코드를 변경해야 하는 번거로움을 피하고 싶을 것입니다.

 해결책

추상 공장 패턴의 첫 번째 방안은 각 제품 패밀리​(제품군)​에 해당하는 개별적인 인터페이스를 명시적으로 선언하는 것입니다. (예: 의자, 소파 또는 커피 테이블). 그다음, 제품의 모든 변형이 위 인터페이스를 따르도록 합니다. 예를 들어, 모든 의자의 변형들은 Chair​(의자) 인터페이스를 구현한다; 모든 커피 테이블 변형들은 Coffee­Table​(커피 테이블) 인터페이스를 구현한다, 등의 규칙을 명시합니다.

같은 객체의 모든 변형은 단일 클래스 계층구조로 옮겨져야 합니다.

추상 공장 패턴의 다음 단계는   을 선언하는 것입니다. 추상 공장 패턴은 제품 패밀리 내의 모든 개별 제품들의 생성 메서드들이 목록화되어있는 인터페이스입니다. (예: create­Chair​(의자 생성), create­Sofa​(소파 생성), create­Coffee­Table​(커피 테이블 생성) 등).

각 구상 팩토리는 특정 제품의 변형에 해당합니다.

다음은 제품 변형을 다룰 차례입니다. 제품 패밀리의 각 변형에 대해 Abstract­Factory 추상 팩토리 인터페이스를 기반으로 별도의 팩토리 클래스를 생성합니다. 팩토리는 특정 종류의 제품을 반환하는 클래스입니다. 예를 들어 Modern­Furniture­Factory​(현대식 가구 팩토리)​에서는 다음 객체들만 생성할 수 있습니다: Modern­Chair​(현대식 의자), Modern­Sofa​(현대식 소파) 및 Modern­Coffee­Table​(현대식 커피 테이블).

클라이언트 코드는 자신에 해당하는 추상 인터페이스를 통해 팩토리들과 제품들 모두와 함께 작동해야 합니다. 그래야 클라이언트 코드에 넘기는 팩토리의 종류와 제품 변형들을 클라이언트 코드를 손상하지 않으며 자유자재로 변경할 수 있습니다.

클라이언트들은 함께 작업하는 팩토리의 구상 클래스에 대해 신경을 쓰지 않아도 돼야 합니다.

클라이언트가 팩토리에 의자를 주문했다고 가정해 봅시다. 클라이언트는 팩토리의 클래스들을 알 필요가 없으며, 팩토리가 어떤 변형의 의자를 생성할지 전혀 신경을 쓰지 않습니다. 클라이언트는 추상 Chair​(의자) 인터페이스를 사용하여, 현대식 의자이든 빅토리아식 의자이든 종류에 상관없이 모든 의자를 항상 동일한 방식으로 주문하며, 그가 의자에 대해 아는 유일한 사실은 제품이 sit­On​(앉을 수 있다) 메서드를 구현한다는 것뿐입니다. 그러나, 생성된 의자의 변형은 항상 같은 팩토리 객체에서 생성된 소파 또는 커피 테이블의 변형과 같을 것입니다.

여기에서 명확히 짚고 넘어가야 할 점이 있습니다. 클라이언트가 추상 인터페이스에만 노출된다면 실제 팩토리 객체를 생성하는 것은 무엇일까요? 일반적으로 프로그램은 초기화 단계에서 구상 팩토리 객체를 생성합니다. 그 직전에 프로그램은 환경 또는 구성 설정에 따라 팩토리 유형을 선택해야 합니다.

 

참고문서: https://velog.io/@jinh2352/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-with-C-Factory-Pattern-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%AC

 

[디자인패턴 with C++] Factory Pattern (추상 팩토리, 함수형 팩토리)

팩토리 메서드나 내부 팩토리 방식은 객체 한 종류를 생성하는 경우에 유용하다. 만약 여러 종류의 연관된 객체들을 생성해야 할 경우는 떻게 해야할까? 바로 추상 팩토리 방식을 사용하면 된다

velog.io

 

/**
 * The Product interface declares the operations that all concrete products must
 * implement.
 */

class Product {
 public:
  virtual ~Product() {}
  virtual std::string Operation() const = 0;
};

/**
 * Concrete Products provide various implementations of the Product interface.
 */
class ConcreteProduct1 : public Product {
 public:
  std::string Operation() const override {
    return "{Result of the ConcreteProduct1}";
  }
};
class ConcreteProduct2 : public Product {
 public:
  std::string Operation() const override {
    return "{Result of the ConcreteProduct2}";
  }
};

/**
 * The Creator class declares the factory method that is supposed to return an
 * object of a Product class. The Creator's subclasses usually provide the
 * implementation of this method.
 */

class Creator {
  /**
   * Note that the Creator may also provide some default implementation of the
   * factory method.
   */
 public:
  virtual ~Creator(){};
  virtual Product* FactoryMethod() const = 0;
  /**
   * Also note that, despite its name, the Creator's primary responsibility is
   * not creating products. Usually, it contains some core business logic that
   * relies on Product objects, returned by the factory method. Subclasses can
   * indirectly change that business logic by overriding the factory method and
   * returning a different type of product from it.
   */

  std::string SomeOperation() const {
    // Call the factory method to create a Product object.
    Product* product = this->FactoryMethod();
    // Now, use the product.
    std::string result = "Creator: The same creator's code has just worked with " + product->Operation();
    delete product;
    return result;
  }
};

/**
 * Concrete Creators override the factory method in order to change the
 * resulting product's type.
 */
class ConcreteCreator1 : public Creator {
  /**
   * Note that the signature of the method still uses the abstract product type,
   * even though the concrete product is actually returned from the method. This
   * way the Creator can stay independent of concrete product classes.
   */
 public:
  Product* FactoryMethod() const override {
    return new ConcreteProduct1();
  }
};

class ConcreteCreator2 : public Creator {
 public:
  Product* FactoryMethod() const override {
    return new ConcreteProduct2();
  }
};

/**
 * The client code works with an instance of a concrete creator, albeit through
 * its base interface. As long as the client keeps working with the creator via
 * the base interface, you can pass it any creator's subclass.
 */
void ClientCode(const Creator& creator) {
  // ...
  std::cout << "Client: I'm not aware of the creator's class, but it still works.\n"
            << creator.SomeOperation() << std::endl;
  // ...
}

/**
 * The Application picks a creator's type depending on the configuration or
 * environment.
 */

int main() {
  std::cout << "App: Launched with the ConcreteCreator1.\n";
  Creator* creator = new ConcreteCreator1();
  ClientCode(*creator);
  std::cout << std::endl;
  std::cout << "App: Launched with the ConcreteCreator2.\n";
  Creator* creator2 = new ConcreteCreator2();
  ClientCode(*creator2);

  delete creator;
  delete creator2;
  return 0;
}