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

전홍영·2024년 3월 3일
0

디자인 패턴

목록 보기
11/12
post-thumbnail

추상 팩토리

추상 팩토리 패턴은 연관성이 있는 객체 군이 여러개 있을 때, 이들을 묶어 추상화하고 어떤 구체적 상황이 주어지면 팩토리 객체에서 집합으로 묶은 객체 군을 구현화하는 생성 패턴이다. 클라이언트는 특정 객체를 사용할 때 팩토리 클래스만을 참조하여 특정 객체에 대한 구현부를 감추어 역할과 구현을 분리시킬 수 있다.
추상 팩토리에서느는 '군'이라는 키워드가 중요한데, 예를 들어 삼성 핸드폰, 노트북, 모니터와 애플 핸드폰 노트북, 모니터 같이 삼성의 제품군이냐 애플의 제품군이냐에 따라 집합이 브랜드 명으로 여러갈래로 나뉘게 될 때, 복잡하게 묶이는 제품군들을 관리와 확장하기 용이하게 패턴화 한것이 추상 팩토리이다.

구조

Abstract Factory

최상위 팩토리 추상 클래스로, 여러개의 제품들을 생성하는 여러 메서드들을 추상화한다.

Concrete Factory

서브 팩토리 클래스들은 타입에 맞는 제품 객체를 반환하는 메서드를 오버라이딩한다.

AbstractProduct

각 타입의 제품들을 추상화한 인터페이스이다.

ConcreteProduct

각 타입의 제품을 구현한 구현체들이다. 이들은 모두 팩토리 객체에서 생성된다.

Client

Client는 추상화된 인터페이스만 이용하여 제품을 받기 때문에 팩토리 구현부나 프로덕트 구현부에 대해서는 알지 못해도 된다.

그럼 추상 팩토리 패턴과 팩토리 메서드 패턴의 차이점은 무엇일까?

두 패턴 모두 팩토리를 통해 구체적인 구현부를 숨기고 객체 생성에 관여하는 패턴 임에 동일하다. 또한 팩토리 클래스가 제품 클래스를 각각 나위어 느슨한 결합 구조를 구성한다는 점에서 공통점을 갖는다.
그러나 두 패턴은 분명한 차이점이 존재한다. 추상 팩토리 패턴은 객체 집합 군에 초점이 맞춰져 있고 팩토리 메서드 패턴은 객체 생성 이후 해야 할 일에 초점이 맞추어져 있다.
팩토리 메서드 패턴은 구체적인 객체 생성과정을 하위 또는 구체 클래스로 옮기는 것이 목적이지만, 추상 팩토리 패턴은 관련 있는 객체를 구체적인 클래스에 의존하지 않고 생성하는 것이 목적이다.

예시 코드로 보는 추상 팩토리

Abstract Factory

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

Concrete Factory

public class ConcreteFactory1 implements AbstractFactory{
    @Override
    public AbstractProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ConcreteProductB1();
    }
}

Abstract Product

public interface AbstractProductA {
    void use();
}

public interface AbstractProductB {
    void use();
}

Concrete Product

public class ConcreteProductA1 implements AbstractProductA {
    @Override
    public void use() {
        System.out.println("Use ConcreteProductA1");
    }
}

public class ConcreteProductB1 implements AbstractProductB {
    @Override
    public void use() {
        System.out.println("Use ConcreteProductB1");
    }
}

Client code

@Test
void 추상_팩토리_패턴_테스트(){
    AbstractFactory factory = null;

    //공장 1 가동
    factory = new ConcreteFactory1();

    //제품 A 생성
    AbstractProductA productA = factory.createProductA();

    //제품 A 사용 - productA1 사용
    productA.use();

    //제품 B 생성
    AbstractProductB productB = factory.createProductB();

    //제품 B 사용 - productB1 사용
    productB.use();
}

// 결과
Use ConcreteProductA1
Use ConcreteProductB1

결과를 보면 알 수 있듯이 클라이언트는 추상 팩토리를 통해 구현부를 알지 않고 추상화에 의존해서 객체를 생성하기 때문에 느슨한 결합도를 가진 채 객체를 생성할 수 있고, 원하는 객체 군 팩토리를 이용하여 객체군에 해당하는 객체를 생성할 수 있게 된다.

구체적인 예시를 통한 추상 팩토리와 팩토리 메서드 코드

예제를 통해 추상 메서드 패턴과 팩토리 메서드 패턴을 알아보고 차이점을 잘 찾아보자. 예는 삼성과 애플의 제품군을 나눈 추상 팩토리와 핸드폰과 스마트워치의 타입에 따른 제품 객체를 생성하도록 재정의하여 걸맞는 제품 객체를 생성하도록 하였다.

추상 팩토리 패턴


이렇게 추상 팩토리 메서드 패턴을 구현하였다. 추상 팩토리 메서드 패턴은 위와 같이 애플군과 삼성군으로 분리하여 각각의 군에 해당하는 객체를 생성하여 반환한다.

팩토리 메서드 패턴


팩토리 메서드 패턴은 한가지 종류의 컴포넌트만 생성하는 구조이다. 그래서 타입에 따른 객체를 생성하도록 분기문을 통해 객체를 생성하는 로직이 필요하다.

두 패턴의 한계

추상 팩터리 패턴과 팩토리 메서드 패턴 모두 한계가 존재한다. 먼저 팩토리 메서드 패턴은 확장시에 유연하게 대처할 수 없다. 왜냐하면 새로운 컴포넌트를 추가하여 확장시에 서브 팩토리마다 새로운 분기문을 추가하여 새로운 컴포넌트를 생성하는 조건을 추가해 주어야 하기 때문에 기존의 코드를 수정해야한다.

예를 들어 삼성, 애플 이외에 샤오밍이라는 객체 군이 생겼을 때 팩토리 메서드 패턴의 팩토리에서는 분기문에 샤오밍에 따른 분기문을 추가하여 기존의 코드를 수정해야 한다. 따라서 OCP를 위배하는 경우가 생기게 된다. 그러나 추상 팩토리 패턴은 기존의 코드의 수정하지 않고 샤오밍에 대한 팩토리와 구현 프로덕트만 추가하면 되기 때문에 확장에는 열려있고 수정에는 닫혀있다고 할 수 있다. 따라서 OCP를 잘 지키고 있다고 할 수 있다.

추상 팩토리 패턴에도 한계는 존재한다. 예를 들어 노트북이라는 컴포넌트가 추가되었을 경우, 추상 팩토리는 기존의 제품군 팩토리의 코드를 수정해야 한다. 반면, 팩토리 메서드 패턴에서 컴포넌트 추가시에는 그에 해당하는 팩토리를 생성하여 확장하면 된다. 이렇듯 두 패턴 모두 각각의 조건에 따라서 한계가 존재하게 된다.

그렇다면 두 패턴을 동시에 쓰면 어떻게 될까? 이 두 패턴은 서로 배타적인 관계라 할 수 없다. 팩토리 메서드는 추상 메서드를 통해 다른 제품 구현과 더불어 객체 생성에 관한 전처리, 후처리를 해주는 로직이 핵심이며, 추상 팩토릴는 여러 타입의 객체 군을 생성할 수 있는 것이 핵심이기 때문에 이 둘을 조합하게 된다면 두 패턴의 장점을 살릴 수 있게 된다.

언제 써야 하나?

추상 팩토리는 관련 제품의 다양한 제품 군과 함께 작동해야 할 때, 해당 제품의 구체적인 클래스에 의존하고 싶지 않을 경우 사용한다. 또한 한 번 구성한 제품군이 변경이 자주 일어날 경우 사용한다.

장단점

위에서 알아봤듯이 클라이언트 코드와 객체 생성 코드를 분리하여 느슨한 결합도를 갖을 수 있게 되어 SRP를 잘 지키게 된다. 또한 제품 군을 쉽게 변경할 수 있어 제품군 변경시 확장에 유연하여 OCP를 지키게 된다.

하지만 한계점이 있어 기존 추상 팩토리에 세부사항이 변경되면 기존 코드를 수정하게 되는 경우가 있고 새로운 종류의 제품을 지우너하는 것이 어렵게 된다.

마지막으로 팩토리 패턴 두가지 모두 팩토리 객체들을 모두 구현해주어야 하기 때문에 객체가 늘어날 때마다 클래스가 증가하여 복잡성이 증가하게 된다는 공통된 단점이 존재한다.

참고 코드
참고 : https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90
참고: https://refactoring.guru/ko/design-patterns/abstract-factory

profile
Don't watch the clock; do what it does. Keep going.

0개의 댓글