추상 팩토리 패턴 (Abstract Factory Pattern)

niireymik·2024년 8월 25일



🏭 Abstract Factory Pattern

추상 팩토리 패턴은 GoF 디자인 패턴의 생성 패턴 중 하나로, 서로 관련된 객체 군 여러 개를 묶어 인터페이스로 추상화하고 이를 구현하는 패턴이다!✨

쉽게 말해, 관련성이 있는 여러 객체들을 만들어주는 인터페이스 : 팩토리 클래스를 두고, 필요할 때 (구체적인 상황이 주어지면) 팩토리 클래스의 객체에서 묶은 객체군을 구현하는 것이다.

추상 팩토리 패턴도 구조도를 함께 볼 텐데, 이는 살짝 복잡할 수 있으니 구체적인 상황으로 추상 팩토리를 먼저 이해한 후 구조도를 살펴보자!


Factory : 제품을 찍어내는 공장!

Builder 패턴에 관해 다룰 때, 잠깐 추상 팩도리를 다룬 적이 있다. 그 때의 설명을 빌리자면 다음과 같다.

(마우스, 키보드, 모니터로 이루어진) 컴퓨터를 제조할 건데, SamsungComputer와 LGComputer 중 어떤 것을 제조할지 입력 받아서 제조한다. 이 ComputerFactory는 삼성 또는 엘지를 입력 받아서 값에 따라 MouseFactory와 KeyboardFactory, MonitorFactory를 생성하고, 각 팩토리는 해당 회사의 마우스/키보드/모니터를 생성하는 것과 같다.

그런데 실제로 추상 팩토리 패턴을 공부해 보니, 다음의 비유가 좀 더 알맞겠다.

  • 추상 팩토리(ComputerFactory)는 컴퓨터를 제조하는 공장이다. 이는 어떤 브랜드의 부품을 만들지는 정하지 않고, 컴퓨터를 제조하기 위해서는 마우스, 키보드, 모니터를 만든다는 사실만 정해둔다. 이를 테면 공장의 설계도같은 것이다.
  • SamsungFactory, LGFactory는 추상 팩토리(ComputerFactory)를 실제로 컴퓨터를 만들어내는 공장이다. 삼성 팩토리는 삼성 마우스, 삼성 키보드, 삼성 모니터를 만들고, 엘지 팩토리는 엘지 마우스, 엘지 키보드, 엘지 모니터를 각각 만든다.
  • 이때, 마우스와 키보드, 모니터도 공장처럼 설계도가 있다. 공장의 설계도를 따라 만들어진 공장이라면, 마우스/키보드/모니터의 정해진 형식을 따라 제조해야 한다.
  • 사용자는 컴퓨터가 필요할 때, 어떤 브랜드의 공장을 사용할지 선택하면, 그 브랜드에 맞는 마우스, 키보드, 모니터로 이루어진 컴퓨터가 완성된다.

즉, 추상 팩토리는 공장 자체의 설계도이고, 구체적인 팩토리는 브랜드별 공장이다. 사용자는 어떤 공장을 사용할지 결정하기만 하면, 나머지는 그 공장이 알아서 처리한다.

(이 정도만 이해하면 추상 팩토리 패턴의 구조도 또한 한눈에 볼 수 있을 것이다!)

여기서 마우스 + 키보드 + 모니터처럼 서로 다르지만 연관이 있는 클래스를 묶어서 제품군이라 칭하고, 제품군을 정의하면 서로 연관된 객체들을 일관된 방식으로 생성할 수 있게 된다!


구조도

구조도의 윗부분부터 보자. 인터페이스(또는 추상 클래스)에 해당하는 AbstractFactory가 있다. 이는 createProductA()createProductB 메서드를 가진다. 이를 구현한 ConcreteFactory1, ConcreteFactory2 클래스는 동일하게 두 메서드를 구현해 실질적인 기능을 하는 클래스이다.

구조도의 맨 아래를 보면, AbstractFactory에서 메서드의 결과로 반환하는 Product를 정의한 AbstractProductA, AbstractProductB 클래스가 있다. 이 또한 AbstractFactory와 같이 실제 구현체가 아닌 인터페이스에 해당한다. 그리고 Factory와 마찬가지로, AbstractProduct를 구현한 ConcreteProductA1, B1, A1, B2가 존재한다. 이들은 ConcreteFactory1, 2에서 실제로 만들어내는 Product이다.

  • AbstractFactory
    • 추상 팩토리 : 실제 팩토리의 공통 인터페이스
    • 예시의 ComputerFactory에 해당
  • ConcreteFactory
    • 구체적인 팩토리 클래스로, AbstractFactory 클래스의 추상 메서드를 오버라이딩함
    • 예시의 SamsungFactory, LGFactory에 해당
  • AbstractProduct
    • 제품의 공통 인터페이스
    • 예시의 마우스, 키보드, 모니터 설계도에 해당
  • ConcreteProduct
    • 구체적인 팩토리 클래스에서 생성되는 구체적인 제품
    • 예시의 삼성 마우스, ... 엘지 모니터에 해당

🎯 추상 팩토리 패턴 용례

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

패턴의 장단점

  • 장점
    • 객체를 생성하는 코드를 분리하여 클라이언트 코드와 결합도를 낮출 수 있다.
    • 제품 군을 쉽게 대체할 수 있다.
    • 단일 책임의 원칙을 준수한다.
    • 개방 / 폐쇄의 원칙을 준수한다.
  • 단점
    • 각 구현체마다 팩토리 객체들을 모두 구현해주어야 하기 때문에 객체가 늘어날 때마다 클래스가 증가하여 코드의 복잡성이 증가한다. (이는 팩토리 패턴의 공통적인 문제점이기도 하다.)
    • 기존 추상 팩토리의 세부사항이 변경되면 모든 팩토리에 대한 수정이 필요해진다. 이는 추상 팩토리와 모든 서브클래스의 수정을 가져온다.
    • 새로운 종류의 제품을 지원하는 것이 어렵다. 새로운 제품이 추가되면 팩토리 구현 로직 자체를 변경해야 한다.



🫧 추상 팩토리 패턴 구현

구현은 앞서 예시로 들었던 모니터, 키보드, 마우스를 생산하는 컴퓨터 공장의 관계를 표현했고, 구조도에서 나타난 흐름을 그대로 적용했다!


추상 팩토리와 그 구현체를 보기 전에, 모니터, 키보드, 마우스를 나타낸 코드부터 보자!
→ 구조도와 비슷하게 추상 프로덕트 & 그 구현체로 나뉜 것을 볼 수 있다 :>

🖥️ 추상 프로덕트

public interface AbstractKeyboard {}
public interface AbstractMonitor {}
public interface AbstractMouse {}

🖥️ 프로덕트 구현체1 - 삼성의 모니터 / 키보드 / 마우스

public class SamsungMonitor implements AbstractMonitor{}
public class SamsungKeyboard implements AbstractKeyboard{}
public class SamsungMouse implements AbstractMouse{}

🖥️ 프로덕트 구현체2 - LG의 모니터 / 키보드 / 마우스

public class LGMonitor implements AbstractMonitor{}
public class LGKeyboard implements AbstractKeyboard{}
public class LGMouse implements AbstractMouse{}

→ 구조도의 AbstractProduct와 그 구현체들을 나타낸다. AbstractKeyboard, AbstractMonitor, AbstractMouse라는 인터페이스가 있고, 이 추상적인 인터페이스를 실제 구현한 구현체가 있다. 예시로 들었던 SamsungLG의 모니터/키보드/마우스가 각각 있다.



🏭 추상 팩토리

public interface AbstractComputerFactory {

    public AbstractMonitor createMonitor();
    public AbstractKeyboard createKeyboard();
    public AbstractMouse createMouse();

}

🏭 팩토리 구현체1 - 삼성 팩토리

public class SamsungFactory implements AbstractComputerFactory{

    public SamsungFactory() {
        System.out.println("삼성 팩토리 생성");
    }

    @Override
    public AbstractMonitor createMonitor() {
        System.out.println("삼성 모니터 생성");
        return new SamsungMonitor();
    }

    @Override
    public AbstractKeyboard createKeyboard() {
        System.out.println("삼성 키보드 생성");
        return new SamsungKeyboard();
    }

    @Override
    public AbstractMouse createMouse() {
        System.out.println("삼성 마우스 생성");
        return new SamsungMouse();
    }

}

🏭 팩토리 구현체2 - LG 팩토리

public class LGFactory implements AbstractComputerFactory{
    
    public LGFactory() {
        System.out.println("LG 팩토리 생성");
    }

    @Override
    public AbstractMonitor createMonitor() {
        System.out.println("LG 모니터 생성");
        return new LGMonitor();
    }

    @Override
    public AbstractKeyboard createKeyboard() {
        System.out.println("LG 키보드 생성");
        return new LGKeyboard();
    }

    @Override
    public AbstractMouse createMouse() {
        System.out.println("LG 마우스 생성");
        return new LGMouse();
    }

}

→ 추상팩토리에서 모니터 / 키보드 / 마우스를 제조하는 createMonitor(), createKeyboard(), createMouse() 메서드를 정의해 두었다. 그리고 그 구현체인 Samsung 팩토리와 LG 팩토리가 메서드들을 오버라이딩한다.

이제 팩토리를 실제로 생성해 제품들을 생산해 보자!

public class Main {

	// 팩토리에서 모니터, 키보드, 마우스를 한 번에 생성
    public static void create(AbstractComputerFactory computerFactory) {
    
        computerFactory.createMonitor();
        computerFactory.createKeyboard();
        computerFactory.createMouse();
        
    }

    public static void main(String[] args) {

        // 컴퓨터 팩토리 선언
        AbstractComputerFactory computerFactory;

        // computerFactory : 삼성 팩토리로 설정
        computerFactory = new SamsungFactory();
        create(computerFactory);

        // computerFactory : LG 팩토리로 설정
        computerFactory = new LGFactory();
        create(computerFactory);

    }

}

AbstractComputerFactory형 변수 computerFactory 하나만을 생성했다. 삼성 팩토리와 LG 팩토리를 설정한 다음의 코드를 보면, 서로 똑같은 create 메서드를 사용하고 있다. 하지만 출력 결과는 다음과 같다.

📌 출력 결과

삼성 팩토리 생성
삼성 모니터 생성
삼성 키보드 생성
삼성 마우스 생성
LG 팩토리 생성
LG 모니터 생성
LG 키보드 생성
LG 마우스 생성

→ 동일한 메서드들을 설정하더라도 오버라이딩된 메서드의 호출로 각 공장의 제품이 만들어지는 것을 확인할 수 있다.
→ 제품 군을 쉽게 대체할 수 있다는 추상 팩토리 패턴의 장점이 드러난다!👍




💡 결론

추상 팩토리 패턴은 제품군이라는 키워드가 핵심이라고 생각한다. 동일한 제품군으로 구성되는 무언가 (위 예시에선 컴퓨터) 를 하나의 추상 팩토리로 묶어두는 것이다! 따라서 구체적인 클래스에 의존하지 않고 인터페이스를 사용하며, 다른 구현체로 대체하는 것이 쉽다는 장점이 있으나, 같은 이유로 인터페이스 자체의 변경은 어려우므로 신중히 사용해야 할 듯하다!✌️

0개의 댓글