추상 팩토리 패턴이란
관련된 객체의 집합 또는 서로 의존하거나 연관된 여러 종류의 객체를 생성하는 인터페이스를 제공하는 디자인 패턴입니다.
이 패턴은 구체적인 클래스에 의존하지 않고 서로 관련된 객체들의 군을 생성할 수 있게 해줍니다.
말은 어려워보이지만 별거 아닙니다.
그냥 테이블, 쇼파, 의자 제품을 한 세트로 생산한다고 생각해봅시다.
그 상황에서 고소득층 전용 프리미엄 하이엔드 제품셋트를 만들고 싶을때,
추상 팩토리 패턴이 적용 되어 있을경우 클라이언트 코드에서 단 몇줄만 다르게 코딩하여 생산가능하게끔 해줍니다.
팩토리 메서드 패턴의 구성
1. AbstractFactory
관련된 객체 집합을 생성하는 메서드를 선언한 인터페이스입니다.
구체적인 팩토리 클래스들이 이 인터페이스를 구현하게 됩니다.
2. ConcreteFactory
AbstractFactory 인터페이스를 구현하는 클래스로, 구체적인 제품군을 생성합니다.
각각의 팩토리는 다른 제품군에 대응됩니다.
3. AbstractProduct
제품군 내의 개별 제품에 대한 인터페이스를 정의합니다.
이 인터페이스는 제품이 가져야 할 메서드들을 선언합니다.
4. ConcreteProduct
AbstractProduct 인터페이스를 구현하는 실제 객체들입니다
이 객체들은 ConcreteFactory에 의해 생성됩니다.
추상 팩토리 패턴 다이어그램
우선 추상 팩토리 패턴은 이렇게 구성됩니다.
복잡해보이지만 예제를 보면 별거 아닐겁니다.
예제: 현대식가구, 전통적가구 셋트를 생산을 해보면서 이해하기
// Abstract Product
public interface Chair {
void create();
}
// Abstract Product
public interface Sofa {
void create();
}
// Abstract Product
public interface Table {
void create();
}
의자, 소파, 테이블을 인터페이스로 만들어봤습니다.
// AbstractFactory
public interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
Table createTable();
}
핵심인 추상팩토리에서 각 추상제품 타입의 생성메서드를 선언해주었습니다.
이제부터는 contreteProduct들을 구현해볼겁니다.
// ConcreteProduct - Modern 제품군 의자
public class ModernChair implements Chair{
public void create() {
System.out.println("Modern 제품군 의자 생성!!!");
}
}
// ConcreteProduct - Modern 제품군 소파
public class ModernSofa implements Sofa{
public void create() {
System.out.println("Modern 제품군 소파 생성!!!");
}
}
// ConcreteProduct - Modern 제품군 테이블
public class ModernTable implements Table{
public void create() {
System.out.println("Modern 제품군 테이블 생성!!!");
}
}
현대식을 하나로 묶어주는 팩토리를 아래에 구현해 줄겁니다.
// ConcreteFactory - Modern 제품군
public class ModernFunitureFactory implements FurnitureFactory{
@Override
public Chair createChair() {
return new ModernChair();
}
@Override
public Sofa createSofa() {
return new ModernSofa();
}
@Override
public Table createTable() {
return new ModernTable();
}
}
느낌이 오시나요?
현대식 의자,쇼파,테이블을 하나의 팩토리에 묶어준 느낌입니다.
전통적 가구셋트도 구현해보고 클라이언트 코드까지 작성해보겠습니다.
// ConcreteProduct - Traditional 제품군 의자
public class TraditionalChair implements Chair{
public void create() {
System.out.println("Traditional 제품군 의자 생성!!!");
}
}
// ConcreteProduct - Traditional 제품군 소파
public class TraditionalSofa implements Sofa{
public void create() {
System.out.println("Traditional 제품군 소파 생성!!!");
}
}
// ConcreteProduct - Traditional 제품군 테이블
public class TraditionalTable implements Table{
public void create() {
System.out.println("Traditional 제품군 테이블 생성!!!");
}
}
// Client
public class FurnitureStore {
private FurnitureFactory factory;
// 핵심
public FurnitureStore(FurnitureFactory factory) {
this.factory = factory;
}
public void orderFurniture() {
Chair chair = factory.createChair();
Sofa sofa = factory.createSofa();
Table table = factory.createTable();
chair.create();
sofa.create();
table.create();
}
public static void main(String[] args) {
// 생성자를 이용한 Modern 제품군 create
FurnitureFactory modernFactory = new ModernFunitureFactory();
FurnitureStore store = new FurnitureStore(modernFactory);
store.orderFurniture();
// 생성자를 이용한 Traditional 제품군 create
FurnitureFactory traditionalFactory = new TraditionalFurnitureFactory();
FurnitureStore store2 = new FurnitureStore(traditionalFactory);
store2.orderFurniture();
}
}
저는 라인이 길어져서 편의상 메인쪽에서 인라인 클래스로 작성해봤는데요
보통은 저 부분도 추상클래스로 빼는편입니다.
중요한곳은 생성자로 추상팩토리클래스를 받아서 주입을 해주는 부분입니다.
그렇게 해주면 콘크리트 팩토리를 바꿔 끼우면서 주문을 해줄 수 있게 됩니다.
실무사용 예
스프링에서 메시징 시스템을 추상화하는 ConnectionFactory 인터페이스가 있습니다.
스프링의 메시징 지원은 JMS, AMQP, Kafka 등 다양한 메시징 시스템과의 통합을 위한 추상화 계층을 제공합니다.
이러한 추상화는 추상 팩토리 패턴을 통해 메시징 시스템의 구체적인 구현 세부사항으로부터 애플리케이션을 분리합니다.
1. AbstractFactory - ConnectionFactory인터페이스
이 인터페이스는 연결을 생성하는 메서드를 정의합니다.
이 연결은 특정 메시징 시스템과의 통신을 위해 사용됩니다.
2. ConcreteFactory - JmsConnectionFactory, RabbitConnectionFactory, KafkaConnectionFactory
이 구체적인 팩토리 클래스들은 ConnectionFactory 인터페이스를 구현합니다.
각 클래스는 특정 메시징 프로토콜(JMS, AMQP, Kafka 등)을 사용하여 연결을 생성합니다.
3. AbstractProduct - 메시징 시스템과의 연결을 나타내는 클래스
스프링은 이를 위한 직접적인 추상 클래스를 제공하지 않지만,
Connection 객체는 메시징 시스템과의 연결을 추상화한 것으로 볼 수 있습니다.
4. ConcreteProduct - 각 메시징 프로토콜에 대한 Connection의 구현체
JmsConnection, RabbitConnection, KafkaConnection 등이 있습니다.
이들은 각각의 메시징 시스템과의 연결을 나타냅니다.
5. Client - 메시징 시스템을 사용하는 애플리케이션 코드
클라이언트는 ConnectionFactory를 통해 메시징 시스템과의 연결을 생성하고, 이 연결을 사용하여 메시지를 송수신합니다.
결론
장점
1. 상호 교환 가능성: 클라이언트는 생성 과정에서 생성되는 구체적인 제품의 클래스를 몰라도 제품군을 쉽게 교체할 수 있습니다.
2. 제품 일관성: 한 팩토리가 제공하는 제품들은 스타일이나 테마가 일관되어 호환됩니다.
3. 확장성: 새로운 종류의 제품을 시스템에 추가하려 할 때, 기존 코드를 변경하지 않고 새로운 코드를 추가함으로써 확장할 수 있습니다.
4. 추상화된 계층: 클라이언트 코드는 제품의 구체적인 구현에서 분리되어, 더 유연하고 관리하기 쉬운 코드를 유지할 수 있습니다.
단점
1. 복잡성 증가: 많은 인터페이스와 클래스가 도입되므로, 시스템의 전반적인 복잡성이 증가할 수 있습니다.
2. 확장의 어려움: 제품군에 새로운 종류의 제품을 추가하는 것은 패턴의 모든 클래스에 변경을 요구할 수 있어 확장성이 제한될 수 있습니다.
3. 유지보수: 제품군이 많아질수록 다루어야 하는 구체 팩토리 클래스의 수가 증가하여 유지보수가 어려워질 수 있습니다.
참고자료
GOF 디자인패턴 책
'디자인패턴' 카테고리의 다른 글
[디자인패턴-구조] 어댑터 패턴: 음악플레이어 예제를 통한 GOF 디자인 패턴의 이해 (WITH 자바) (0) | 2024.02.16 |
---|---|
[디자인패턴-생성] 싱글톤 패턴: 싱글톤 인스턴스 생성의 3가지 방법 (WITH 자바) (0) | 2024.02.13 |
[디자인패턴-생성] 프로토타입 패턴: 문서 템플릿 예제를 통한 GOF 디자인 패턴의 이해 (WITH 자바) (0) | 2024.02.12 |
[디자인패턴-생성] 팩토리메서드 패턴: 커피 예제를 통한 GOF 디자인 패턴의 이해(WITH 자바) (0) | 2024.02.10 |
[디자인패턴-생성] 빌더 패턴: 커피 예제를 통한 GOF 디자인 패턴의 이해 (0) | 2024.01.31 |