티스토리 뷰

추상 팩토리 패턴이란?

추상팩토리 패턴은 연관성이 있는 객체 군이 여러개 있을 경우 이들을 묶어 추상화하고, 어떤 구체적인 상황이 주어지면 팩토리 객체에서 집합으로 묶은 객체 군을 구현화하는 생성 패턴이다. 클라이언트에서 특정 객체를 사용할 때 팩토리 클래스만을 참조해 특정 객체에 대한 구현부를 감추어 역할과 구현을 분리시킬 수 있다. 즉, 추상 팩토리의 핵심은 제품'군' 집합을 타입 별로 찍어낼 수 있다는 점이 포인트이다. 예를 들어 모니터, 마우스, 키보드를 묶은 전자 제품군이 있는데 이들을 또 삼성 제품군이냐 애플 제품군이냐 로지텍 제품군이냐에 따라 집합이 브랜드 명으로 나뉘게 될때, 복잡하게 묶이는 이러한 제품군들을 관리와 확장하기 용이하게 패턴화한 것이다.

추상 팩토리 패턴 사용 방법

  • AbstractFactory : 최상위 공장 클래스 인터페이스. 여러개의 제품들을 생성하는 여러 메소드들을 추상화 한다.
  • ConcreteFactory : 서브 공장 클래스들은 타입에 맞는 제품 객체를 반환하도록 메소드들을 재정의한다.
  • AbstractProduct : 각 타입의 제품들을 추상화한 클래스
  • ConcreteProduct (ProductA ~ ProductB) : 각 타입의 제품 구현체들. 이들은 팩토리 객체로부터 생성된다. 
  • Client : Client는 추상화된 인터페이스만을 이용하여 제품을 받기 때문에, 구체적인 제품, 공장에 대해서는 모른다.

 

하나의 팩토리에서 여러 개의 구현체를 만드는 것이 특징이다!


Product 클래스

// Product A 제품군
interface AbstractProductA {
}

// Product A - 1
class ConcreteProductA1 implements AbstractProductA {
}

// Product A - 2
class ConcreteProductA2 implements AbstractProductA {
}
// Product B 제품군
interface AbstractProductB {
}

// Product B - 1
class ConcreteProductB1 implements AbstractProductB {
}

// Product B - 2
class ConcreteProductB2 implements AbstractProductB {
}

 

Factory 클래스

interface AbstractFactory {
    AbstractProductA createProductA();
    AbstractProductB createProductB();
}

// Product A1와 B1 제품군을 생산하는 공장군 1 
class ConcreteFactory1 implements AbstractFactory {
    public AbstractProductA createProductA() {
        return new ConcreteProductA1();
    }
    public AbstractProductB createProductB() {
        return new ConcreteProductB1();
    }
}

// Product A2와 B2 제품군을 생산하는 공장군 2
class ConcreteFactory2 implements AbstractFactory {
    public AbstractProductA createProductA() {
        return new ConcreteProductA2();
    }
    public AbstractProductB createProductB() {
        return new ConcreteProductB2();
    }
}
class Client {
    public static void main(String[] args) {
    	AbstractFactory factory = null;
        
        // 1. 공장군 1을 가동시킨다.
        factory = new ConcreteFactory1();

        // 2. 공장군 1을 통해 제품군 A1를 생성하도록 한다 (클라이언트는 구체적인 구현은 모르고 인터페이스에 의존한다)
        AbstractProductA product_A1 = factory.createProductA();
        System.out.println(product_A1.getClass().getName()); // ConcreteProductA1

        // 3. 공장군 2를 가동시킨다.
        factory = new ConcreteFactory2();

        // 4. 공장군 2를 통해 제품군 A2를 생성하도록 한다 (클라이언트는 구체적인 구현은 모르고 인터페이스에 의존한다)
        AbstractProductA product_A2 = factory.createProductA();
        System.out.println(product_A2.getClass().getName()); // ConcreteProductA2
    }
}

패턴 사용 시기

  • 관련 제품의 다양한 제품 군과 함께 작동해야 할 때, 해당 제품의 구체적인 클래스에 의존하고 싶지 않은 경우
  • 여러 제품군 중 하나를 선택해서 시스템을 설정해야 하고 한 번 구성한 제품을 다른 것으로 대체할 수도 있을 때
  • 제품에 대한 클래스 라이브러리를 제공하고, 그들의 구현이 아닌 인터페이스를 노출시키고 싶을 때

패턴 장점

  • 객체를 생성하는 코드를 분리하여 클라이언트 코드와 결합도를 낮출 수 있다.
  • 제품 군을 쉽게 대체 할 수 있다.
  • 프로덕트 사이의 일관성을 증진시킬 수 있다.
  • 단일 책임 원칙 준수
  • 개방 / 폐쇄 원칙 준수

패턴 단점

  • 각 구현체마다 팩토리 객체들을 모두 구현해주어야 하기 때문에 객체가 늘어날때 마다 클래스가 증가하여 코드의 복잡성이 증가한다. (팩토리 패턴의 공통적인 문제점)
  • 기존 추상 팩토리의 세부사항이 변경되면 모든 팩토리에 대한 수정이 필요해진다. 이는 추상 팩토리와 모든 서브클래스의 수정을 가져온다. 
  • 새로운 종류의 제품을 지원하는 것이 어렵다. 새로운 제품이 추가되면 팩토리 구현 로직 자체를 변경해야한다.

추상 팩토리 vs 팩토리 메소드

  팩토리 메소드 패턴 추상 팩토리 패턴
공통점 객체 생성 과정을 추상화한 인터페이스를 제공
객체 생성을 캡슐화함으로써 구체적인 타입을 감추고 느슨한 결합 구조를 표방
차이점 하위 클래스가 객체 생성을 결정 관련된 객체의 패밀리를 생성하는 인터페이스를 제공
한 종류의 객체만을 생성 서로 관련된 여러 종류의 객체를 생성
객체 생성 방식의 변화에 더 유연 제품군 전체의 변경이 필요할 때 유연성을 제공
클라이언트 코드와 인스턴스를 만드는 코드를 분리해야 할 때 사용 서로 관련 있는 여러 객체 군을 일관되게 생성해야 할 때 사용