추상 팩토리(Abstract Factory Pattern)
큰 큐모의 객체군을 형성하는 생성 패턴
유사성
팩토리, 팩토리 메서드, 추상 팩토리는 서로 매우 유사한 생김새와 성격을 가짐.
차이점
- 팩토리 vs 팩토리 메서드 : 추상화 여부.
- 팩토리 메서드 vs 추상 팩토리 : 추상화된 그룹 형성 및 관리 여부.
상속과 다형성
- 일반적 상속 :
상위 클래스의 내용을 포함하는 객체의 확장
- 추상 클래스의 상속 :
하위 클래스의 구체적 행위를 규정하는 선언
- 추상화 : 하위 클래스에 객체지향의 다형성 부여
- 다형성 부여
- 형태와 틀은 같지만 실제 처리 내용은 다르게 클래스로 생성 가능하다.
- 여러 개의 하위 클래스를 만들어야 한다는 단점도 있다.
- 매개변수를 통한 선택적 처리 방법
→ 추상화와 다형성의 단점을 보완할 수 있다.
하지만, 객체지향의 특징인 다형성을 완전히 배제할 순 없다.
그룹
추상 팩토리 ≒ 여러 팩토리 메서드를 그룹으로 묶은 것
→ 생성 패턴을 그룹 형태로 변경
추상 클래스
- 팩토리 메서드
- 팩토리 → 추상화 ⇒ 클래스의 선언과 구현 분리
- 추상화
- 인터페이스의 특징
- 실체적 객체의 생성 동작을 하위 클래스에 위임
- 하위 클래스에서 상위 추상 클래스에 선언된 인터페이스에 따라 메서드 구현
- 구현되는 메서드 : 상위 추상 메서드를 오버라이드
#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) 다국적 자동차 공장의 한국, 미국 생산 지역을 그룹화
#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
#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) 기존 팩토리 메서드를 추상 팩토리로 확장
#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상속
#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 부품 관련 코드를 삽입
⇒ 추상 클래스
- 그룹을 나누는 것은 쉬움
- 서브 객체를 추가하는 것은 어려움
💡 하나의 패턴이 문제를 완벽히 해결할 수 있는 것은 아님.
패턴 적용 시, 장단점을 잘 파악해서 적절히 사용하는 것이 중요.
장점, 단점
장점
- 생성 패턴을 독립적으로 동작하도록 분리, 분리된 하나의 그룹 별로 객체를 선택해 생성.
- 추상 팩토리의 그룹 : 서로 동일한 처리 로직을 갖고 있음.
→ 다른 그룹으로 변경되어도 하위 클래스를 통해 선택적 객체를 다르게 생성 가능.
- 큰 변화 없이 시스템의 군(group)을 생성, 변경 가능.
- 추상 팩토리의 일부 - 인터페이스와 같은 역할.
인터페이스 - 코드를 일관적으로 유지, 실제 구현을 다르게 실행시킬 수 있다.
단점
- 새로운 종류의 군(group) 추가가 쉽지 않다.
- 기존 군에서 새로운 군을 추가하여 확장할 때,
모든 서브 클래스들이 동시에 변경되어야 하는 추상 팩토리의 특징.
→ 새로운 클래스 제품군 추가 → 클래스 제품에 대한 구조 설계
→ 추상 팩토리 구조에 등록
⇒ 매번 새로운 종류 추가 시마다 구조를 재설계하는 것 = 확장성이 떨어짐.
- 추상 팩토리 그룹
- 계층적 구조 → 계층을 확장하며 그룹을 관리
- 팩토리 메서드와 유사하나 관리할 그룹이 많음.
- 또한, 계층의 크기가 커짐
⇒ 복잡한 문제가 발생할 수 있다.
정리
- 추상 팩토리
- 팩토리 메서드 패턴을 포함하며 팩토리 부분을 추상화해 그룹으로 확장
- 생성된 그룹을 통해 전체를 쉽게 변경 가능
- 객체 생성 과정 = 프로세스 공정
같은 방식으로 생성할 때 적용하면 좋은 패턴