GoF 디자인 패턴 6 - [생성] 추상 팩토리 패턴

김정환·2024년 9월 3일
0

GoF 디자인패턴

목록 보기
6/9

추상 팩토리(Abstract Factory Pattern)


큰 큐모의 객체군을 형성하는 생성 패턴

유사성

팩토리, 팩토리 메서드, 추상 팩토리는 서로 매우 유사한 생김새와 성격을 가짐.

차이점

  • 팩토리 vs 팩토리 메서드 : 추상화 여부.
  • 팩토리 메서드 vs 추상 팩토리 : 추상화된 그룹 형성 및 관리 여부.

상속과 다형성

  • 일반적 상속 :
    상위 클래스의 내용을 포함하는 객체의 확장
  • 추상 클래스의 상속 :
    하위 클래스의 구체적 행위를 규정하는 선언
  • 추상화 : 하위 클래스에 객체지향의 다형성 부여
    • 다형성 부여
      • 형태와 틀은 같지만 실제 처리 내용은 다르게 클래스로 생성 가능하다.
      • 여러 개의 하위 클래스를 만들어야 한다는 단점도 있다.
  • 매개변수를 통한 선택적 처리 방법
    → 추상화와 다형성의 단점을 보완할 수 있다.
    하지만, 객체지향의 특징인 다형성을 완전히 배제할 순 없다.

그룹


추상 팩토리 ≒ 여러 팩토리 메서드를 그룹으로 묶은 것
→ 생성 패턴을 그룹 형태로 변경

추상 클래스

  • 팩토리 메서드
    • 팩토리 → 추상화 ⇒ 클래스의 선언과 구현 분리
  • 추상화
    • 인터페이스의 특징
      1. 실체적 객체의 생성 동작을 하위 클래스에 위임
      2. 하위 클래스에서 상위 추상 클래스에 선언된 인터페이스에 따라 메서드 구현
      3. 구현되는 메서드 : 상위 추상 메서드를 오버라이드
// AbstractFactory.h
#include "AbstractTire.h"
#include "AbstractDoor.h"

#ifndef ABSTRACT_FACTORY_H
#define ABSTRACT_FACTORY_H

class AbstractFactory
{
public:
    AbstractFactory(){};
    ~AbstractFactory(){};
    virtual AbstractTire create_tire(){ return AbstractTire(); };
    virtual AbstractDoor create_door(){ return AbstractDoor(); };
};

#endif
  • 추상 클래스에 2개의 부품 객체 생산 메서드 선언
    • 추상 메서드 ≒ 인터페이스

다형성을 이용한 군집 형성

  • 추상화 → 다형성 적용 → 여러 개의 하위 클래스 생성 가능
    • 팩토리 메서드 : 추상 클래스 - 하위 클래스 1개로만 구성
    • 추상 팩토리 : 다형성을 적극 활용 → 다수의 하위 클래스를 생성, 관리하는 특징
  • 추상 클래스 상속하기
    • 다형성 적용 가능
    • 하위 클래스는 위임된 동작을 서로 다르게 정의 가능
  • 상속을 통한 다형성 적용 : 하위 클래스가 하나의 군(group)으로 형성될 수 있다.
  • 추상 팩토리
    • 추상화의 다형성 적용 → 객체 생성군을 형성

    • 추상화와 다형성을 이용 → 집단 단위의 객체 생성을 관리할 수 있다.

      ⇒ 추상화와 상속을 극대화해 다형성의 특징을 응용한다.

공장

  • 추상 팩토리 (Abstract Factory)
    • 다양한 객체 생성 과정에서 그룹화가 필요할 때 유용 : 공장의 개념을 추상화
    • 단위 객체들을 생산하는 공장 시스템

팩토리 그룹


  • 팩토리 메서드 : 추상화 과정을 적용하나 단일 그룹으로 제한한다.
  • 추상 팩토리 : 추상화를 통해 그룹 생성 가능하다.

그룹

  • 생성 패턴의 목적
    • 클래스의 객체 생성 처리를 묶어서 관리하는 것
    • 팩토리, 팩토리 메서드 또한 객체 생성을 하나의 클래스로 묶어서 처리
      • 팩토리 메서드
        • 추상화 적용 → 단일 클래스를 분리 & 확장
        • 추상화된 클래스 → 여러 개의 하위 클래스에 상속한다.

추상 클래스 상속 구조

ex) 유사한 객체의 생성을 묶어 2개 이상의 하위 클래스로 상속해 동작을 구분할 수 있다.

→ 분리된 하위 클래스는 공통된 상위 클래스를 상속 받을 수 있다.

  • 유사한 생성을 담당하는 하위 클래스들 ⇒ 각각의 고유 그룹으로 분류 가능
    • 팩토리 메서드 : 하나의 하위 클래스만
    • 추상 팩토리 : 복수의 하위 클래스 구성 가능

가상화 관점의 추상화

  • 추상 팩토리, 팩토리 메서드 - 모두 단일 클래스를 추상화
    • 추상화 사용 이유 : 구체적인 객체 생성 로직을 알지 못하기 때문.
  • 구체적인 내용을 알 수 없다.

  • 구현 로직을 알 수 없다 → “가상화”를 통해 이 부분을 대체한다.
  • 추상화에서 “가상” :
    인터페이스를 적용해 실체 객체 생성은 하위 클래스에서 담당한다.
  • 추상 팩토리 = 복수의 팩토리 메서드 패턴을 결합한 것.
    → 하나의 가상화된 객체 생성군 형성.

⇒ 가상화 그룹을 2개 이상(복수) 처리

하위 클래스 : 한국 공장

  • 앞선 추상 팩토리의 상위 클래스 - 자동차 공장을 하위 클래스로 구현
  • ex) 다국적 자동차 공장의 한국, 미국 생산 지역을 그룹화
// ConcreteFactoryKor.h
#include "AbstractFactory.h"

#ifndef CONCRETE_FACTORY_KOR_H
#define CONCRETE_FACTORY_KOR_H

class ConcreteFactoryKor : public AbstractFactory
{
public:
    ConcreteFactoryKor();
    ~ConcreteFactoryKor();
		// 구현부
    AbstractTire create_tire();
    AbstractDoor create_door();
};

#endif // MACRO
// ConcreteFactoryKor.cpp

#include "ConcreteFactoryKor.h"
#include "ConcreteTireKor.cpp"
#include "ConcreteDoorKor.cpp"

ConcreteFactoryKor::ConcreteFactoryKor(){
    cout << "new Concrete Factory Kor\n";
}
ConcreteFactoryKor::~ConcreteFactoryKor(){}

AbstractTire ConcreteFactoryKor::create_tire(){
    return ConcreteTireKor();
}

AbstractDoor ConcreteFactoryKor::create_door(){
    return ConcreteDoorKor();
}
💡 하나의 객체군 작성은 팩토리 메서드와 동일
  • 하위 클래스는 조건을 매개변수 처리하지 않음.
    → 조건문 없이 직접 2개의 객체 생성 매서드를 작성하고 메서드명으로 필요한 객체를 생성.

공장 추가


  • 추상 팩토리 : 그룹을 통해 복수의 공장 생성 가능.
  • 사용 목적 : 복수의 객체 생성을 담당하는 군집 관리.

프로젝트

  • 프로젝트 규모가 커진다 → 많은 클래스 파일과 객체가 요구됨.
    • 팩토리, 팩토리 메서드 패턴 = 필요한 객체 생성을 한 곳으로 모은 후 관리하는 것이 편리
    • 더 큰 규모의 프로젝트 수행 시,
      하나의 생성 패턴으로 모든 객체를 생성 처리하는 것이 좋음.

⇒ 이때 유사한 객체의 생성을 그룹화하여 처리

= 추상 팩토리 개념 : 생성 패턴을 그룹화하기 위해 도입된 것.

💡 추상 팩토리 : 복잡하고 규모가 큰 프로젝트를 수행하기 위해 복수의 생성 패턴의 그룹을 처리, 다수의 독립적 그룹을 형성.

그룹 추가

  • 팩토리 메서드 vs 추상 팩토리
    • 팩토리 메서드 : 단일 그룹 관리
    • 추상 팩토리 : 복수의 그룹 관리
  • 추상화를 통한 그룹 형성
    • 추상 팩토리에서 새 그룹을 만드는 것 =
      앞 예제에서 객체의 생산 공장을 추가하는 것.

  • 상위의 추상 클래스를 상속받을 하위 클래스를 추가로 생성 → 추상 클래스에서 정의한 인터페이스 규격에 맞게 새로운 그룹의 내용 구현 → 하위 클래스는 추상화 규약에 따라 실제 처리 로직을 자유롭게 작성
    (추상화는 인터페이스와 같이 별도의 로직을 정의하지 않음)

⇒ 이러한 특징이 적용되어 독립된 그룹을 형성할 수 있다.

하위 클래스 : 미국 공장

  • 복수의 그룹 : 팩토리 메서드 패턴이 중복되어 적용되는 것을 의미
    → 추상 팩토리 = 여러 개의 공장이 있는 생산 단지
  • ex) 기존 팩토리 메서드를 추상 팩토리로 확장
//ConcreteFactoryUSA.h
#include "AbstractFactory.h"

#ifndef CONCRETE_FACTORY_USA_H
#define CONCRETE_FACTORY_USA_H

class ConcreteFactoryUSA : public AbstractFactory
{
public:
    ConcreteFactoryUSA();
    ~ConcreteFactoryUSA();
    AbstractTire create_tire();
    AbstractDoor create_door();
};

#endif
  • 앞선 ConcreteFactoryKor과 마찬가지로 AbstractFactory상속
//ConcreteFactoryUSA.cpp
#include "ConcreteFactoryUSA.h"
#include "ConcreteTireUSA.cpp"
#include "ConcreteDoorUSA.cpp"

ConcreteFactoryUSA::ConcreteFactoryUSA(){
    cout << "new Concrete Factory USA\n";
}
ConcreteFactoryUSA::~ConcreteFactoryUSA(){}

AbstractTire ConcreteFactoryUSA::create_tire(){
    return ConcreteTireUSA();
}

AbstractDoor ConcreteFactoryUSA::create_door(){
    return ConcreteDoorUSA();
}
  • ConcreteFactoryUSA는 추상 클래스 AbstractFactory를 상속하며 다형성이 적용된 상태
    • 상위 추상 클래스에서 선언된 추상 메서드를 구현하고
      추상 메서드에서는 별도로 분리된 객체를 생성.
  • 하나의 부품을 한국, 미국 공장에서 같이 사용할 수도 있으나
    규격차이로 나누어 적용한다고 가정
    → 이를 공장별로 그룹화하여 분리.

객체 분리를 활용한 은닉성 활용

  • 추상 팩토리 : 추상 클래스를 응용한 객체 구현 방법 사용
    • 추상화

      → 인터페이스 전달

      → 상속을 통해 실제 로직을 하위 클래스에 구현

    • 인터페이스

      → 실제로 생성되는 로직을 하위 클래스에서 구현

      ⇒ 실제 동작하는 구현부를 외부로부터 감출 수 있다.

      (= 객체지향의 특징 : 은닉성)

  • 추상화를 통한 은닉성

- 추상 팩토리 - 객체를 분리 → 은닉성을 적용
    
    → 분리된 하위 클래스 → 독립적은 그룹을 생성
    
    ⇒ 외부로부터 감출 수 있다.
    

목적성

  • 추상 팩토리
    • 특정 클래스에 의존하지 않는다.
    • 해결해야 하는 문제에 따라 객체 생성 그룹을 형성한다.
      ⇒ 문제가 다양할 경우, 새로운 객체를 형성해 그룹을 추가한다.
    • 해결해야 할 주제가 변할 때마다 목적(문제)에 따른 그룹을 변경하여 적용
      ⇒ 각각의 그룹들은 서로 호환성을 가지며 동일한 인터페이스에 의해 호출한다.
  • 동일한 인터페이스로 다른 목적을 수행한다.

  • 추상 팩토리는 이런 목적성에 따라 그룹 간 호환성을 유지한다.
  • 목적이 변경될 경우 필요한 객체를 생성하는 그룹을 변경할 수 있다. → 객체 생성을 담당하는 클래스와 이를 호출하는 클라이언트 클래스를 분리할 수 있기 때문.

부품 추가


추상 팩토리를 적용해 실제 생성하는 객체 클래스를 설계

부품 추상화

  • 앞선 예제에서 2개의 추상 팩토리 그룹을 작성함.

  • Tire와 Door 객체를 생성하는 것이 두 그룹의 공통 기능.
    하지만, 두 그룹에서 생성되는 Tire와 Door 객체는 약간의 차이가 필요한 경우가 발생했다고 가정.
    ⇒ 두 그룹에서 직접 Tire와 Door 객체를 생성하지 않고 공통된 부분의 호환성을 위해 추상화 진행.


  • 추상 클래스는 직접 구현하지 않고 하위 클래스에 위임한다. ⇒ 추상 클래스와 같이 공통된 인터페이스를 적용하면
    은닉성 보장 + 목적에 맞는 독립된 설계를 다양하게 진행 가능하다.

※ 단, 단순한 인터페이스만 상속할 땐 추상 클래스보다 인터페이스를 통해 구현하는 것이 더 나을 수 있다.

추상 팩토리 구현부

  • 앞선 예제의 추상 팩토리는 2개의 객체를 생성 후 반환한다.

  • 앞에서 선언한 추상 클래스를 상속
    → 추상 메서드 구현
    → 부품의 하위 클래스를 객체로 생성

  • Concrete Factory의 흐름도

    • 생성 클래스를 추상 팩토리에서 분리된 그룹별로 독립해서 설계 가능
  • 이처럼 추상 팩토리
    → 객체의 생성을 그룹화해 관리
    → 그룹화 과정에서 생성되는 클래스들은 유사한 구조로 형성

  • 추상 클래스 이용 시
    → 여러 공장에서 제품을 생산하듯 복수의 생성 그룹 적용 가능
    → 각각 그룹 - 목적에 맞는 객체를 독립적으로 생성 가능

새로운 부품

  • 추상 팩토리 - 생성 패턴을 그룹화된 구조로 분리한다.
    • 추상 클래스를 상속받은 하위 클래스 추가 → 새로운 그룹 생성이 쉬워진다.
    • 단, 그룹의 하위 클래스 추가는 쉽지 않음. ex) 앞선 예제에서 각 그룹의 호환성 유지를 위해 실체 객체(tire, door)도 추상화 ex) 각 그룹에 engine 이라는 새 부품 추가 시,
      추가 클래스 그룹으로 분리된 모든 클래스에 engine 부품 관련 코드를 삽입

⇒ 추상 클래스

  • 그룹을 나누는 것은 쉬움
  • 서브 객체를 추가하는 것은 어려움
💡 하나의 패턴이 문제를 완벽히 해결할 수 있는 것은 아님. 패턴 적용 시, 장단점을 잘 파악해서 적절히 사용하는 것이 중요.

장점, 단점


장점

  1. 생성 패턴을 독립적으로 동작하도록 분리, 분리된 하나의 그룹 별로 객체를 선택해 생성.
  2. 추상 팩토리의 그룹 : 서로 동일한 처리 로직을 갖고 있음.
    → 다른 그룹으로 변경되어도 하위 클래스를 통해 선택적 객체를 다르게 생성 가능.
  3. 큰 변화 없이 시스템의 군(group)을 생성, 변경 가능.
  4. 추상 팩토리의 일부 - 인터페이스와 같은 역할.
    인터페이스 - 코드를 일관적으로 유지, 실제 구현을 다르게 실행시킬 수 있다.

단점

  1. 새로운 종류의 군(group) 추가가 쉽지 않다.
  • 기존 군에서 새로운 군을 추가하여 확장할 때,
    모든 서브 클래스들이 동시에 변경되어야 하는 추상 팩토리의 특징.
    → 새로운 클래스 제품군 추가 → 클래스 제품에 대한 구조 설계
    → 추상 팩토리 구조에 등록
    ⇒ 매번 새로운 종류 추가 시마다 구조를 재설계하는 것 = 확장성이 떨어짐.
  1. 추상 팩토리 그룹
  • 계층적 구조 → 계층을 확장하며 그룹을 관리
  • 팩토리 메서드와 유사하나 관리할 그룹이 많음.
  • 또한, 계층의 크기가 커짐
    ⇒ 복잡한 문제가 발생할 수 있다.

정리


  • 추상 팩토리
    • 팩토리 메서드 패턴을 포함하며 팩토리 부분을 추상화해 그룹으로 확장
    • 생성된 그룹을 통해 전체를 쉽게 변경 가능
    • 객체 생성 과정 = 프로세스 공정
      같은 방식으로 생성할 때 적용하면 좋은 패턴
profile
만성피로 개발자

0개의 댓글