자바 환경에서 객체 인스턴스화를 제어하는 단순 팩토리 패턴 가이드

객체 생성의 복잡성과 분리 필요성

소프트웨어 개발 과정에서 가장 빈번하게 접하는 패턴 중 하나는 객체를 어떻게 생성하느냐에 관한 것입니다. 생성형 패턴은 시스템의 유연성을 확보하기 위한 핵심 도구입니다. 그중에서도 '단순 팩토리(Simple Factory)'는 개념적으로 접근하기 가장 쉬운 시작점이며, 실제 프로젝트에서도 널리 쓰입니다. 이 패턴은 GoF(가디언 오프라인) 의 공식 23 가지 패턴 목록에는 포함되지 않았지만, 다른 고급 공장 패턴들을 이해하는 전제 조건으로 기능합니다.

예를 들어, 기업용 데이터 시각화 도구를 개발한다고 가정해 봅시다. 사용자가 바 차트 (Bar Chart), 파이 차트 (Pie Chart), 선 그래프 (Line Graph) 등을 선택하여 데이터를 표현해야 하는 상황이 있습니다. 초기 설계 단계에서 모든 기능을 하나의 대륙 같은 클래스 안에 구현한 경우를 살펴보면 다음과 같은 문제가 발생합니다.


public class DataVisualizer {
    private String visualType;

    public DataVisualizer(double[] sourceData, String mode) {
        this.visualType = mode;
        // 생성 시점에 수많은 조건 분기로 로직이 오염됨
        if ("bar".equals(mode)) { /* ... */ } 
        else if ("pie".equals(mode)) { /* ... */ }
        // ... 추가 타입 발생 시 코드 수정 필요
    }

    public void render() {
        // 사용 시에도 반복되는 조건문 확인 필요
        if ("bar".equals(visualType)) { /* ... */ }
        // ...
    }
}

위와 같이 구현된 클래스는 '단일 책임 원칙 (Single Responsibility Principle)'을 심각하게 위반합니다. 객체의 생성 로직과 비즈니스 처리 로직이 한데 뒤섞여 있어 유지보수 비용이 급증하며, 새로운 차트 유형을 추가할 때마다 소스 코드를 변경해야 하는 '개방 - 폐쇄 원칙 (Open/Closed Principle)' 위반의 원인이 됩니다.

단순 팩토리의 구조적 정의

이러한 문제점을 해결하기 위해 객체 생성 과정을 외부로 추상화하는 것이 단순 팩토리 패턴입니다. 핵심 아이디어는 클라이언트가 구체적인 클래스 이름을 몰라도, 적절한 파라미터만 전달하면 원하는 객체를 받아오도록 하는 것입니다.

패턴 내부에서는 세 가지 주요 역할이 정의됩니다:

  • 추상 제품 (Abstract Product): 모든 구체 객체가 상속하거나 구현해야 하는 공통 인터페이스입니다. (예: Visualizable)
  • 구체 제품 (Concrete Product): 실제로 동작하는 개별 객체들입니다. (예: BarRenderer, PieRenderer)
  • 공장 (Factory): 매개변수에 따라 어떤 구체 제품을 생성할지 결정하고 반환하는 정적 메서드를 보유한 클래스입니다.

이 구조를 통해 클라이언트는 직접 new 연산자를 사용하지 않아도 되며, 생성 로직의 변경사항으로부터 보호받을 수 있습니다.

실무 적용 예시: 리포트 빌더 개선

초기 설계를 개선하여 계층 구조를 명확히 한 모습을 확인해 보겠습니다. 이전의 단일 클래스 대신 인터페이스와 독립적인 구현 클래스로 분리하고, 별도의 관리자가 객체 생성을 담당하도록 변경했습니다.


// 공통 규격 정의
public interface ReportElement {
    void executeDraw();
}

// 구체 구현: 막대그래프
public class BarPlot implements ReportElement {
    @Override
    public void executeDraw() {
        System.out.println("막대형태 데이터 렌더링 수행");
    }
}

// 구체 구현: 원형 그래프
public class PiePlot implements ReportElement {
    @Override
    public void executeDraw() {
        System.out.println("원형 형태 데이터 렌더링 수행");
    }
}

// 객체 생성 관리자
public class ElementProvider {
    
    public static ReportElement createInstance(String kind) {
        ReportElement target = null;
        
        if ("bar".equalsIgnoreCase(kind)) {
            target = new BarPlot();
            applyDefaultSettings(target);
        } else if ("pie".equalsIgnoreCase(kind)) {
            target = new PiePlot();
            applyDefaultSettings(target);
        }
        
        return target;
    }
    
    private static void applyDefaultSettings(ReportElement elem) {
        // 공통 초기화 로직 처리
    }
}

클라이언트 측 코드는 이제 훨씬 간결해집니다.


public class ClientApp {
    public static void main(String[] args) {
        ReportElement chart = ElementProvider.createInstance("bar");
        chart.executeDraw();
    }
}

설정 파일 활용을 통한 결합도 최소화

위 방식은 여전히 클라이언트 코드에 문자열 리터럴 ("bar", "pie" 등) 이 노출되어 있습니다. 만약 런타임 설정이나 외부 구성 정보를 통해 어떤 객체를 생성할지 결정하고 싶다면? 이때는 프로그래밍 언어 내의 상수를 사용하는 대신 외부 설정 파일 (XML, Properties, JSON 등) 을 참조하는 방식을 도입합니다.

예를 들어, config.xml 에 생성할 대상 타입을 저장하고 이를 파싱하는 유틸리티 클래스를 만드는 것입니다.

<configuration>
    <targetType>bar</targetType>
</configuration>

유틸리티 코드가 설정 파일 값을 읽어오고, 공장 메서드로 넘겨주는 형태로 구현될 경우, 특정 객체 종류를 변경하더라도 클라이언트 메인 로직을 재컴파일하지 않고 파일 내용만 수정하면 됩니다. 이는 시스템의 가변성에 중요한 기여를 합니다.

패턴 최적화: 통합형 팩토리

간혹 시스템 규모가 크지 않거나 구조적 단순화가 필요한 경우에는, 별도의 팩토리 클래스 없이 추상 제품 클래스 내부에 정적 팩토리 메서드를 제공하는 방식으로 변형하기도 합니다. 이렇게 하면 클래스 수가 줄어든다는 장점이 있으나, 확장성이 제한될 수 있음을 고려해야 합니다. 많은 표준 라이브러리나 프레임워크가 이러한 방식을 채택하고 있기도 합니다.

적합성과 주의사항

단순 팩토리 패턴은 강력한 장점과 함께 몇 가지 단점을 가지고 있어 사용 상황에 따른 신중한 판단이 필요합니다.

사용 권장 상황

  • 생성해야 할 객체 종류가 매우 적은 경우
  • 클라이언트가 생성 세부사항을 알 필요가 없는 경우
  • 빠른 프로토입 개발이나 소규모 모듈인 경우

주의할 점

  • 책임 집중: 모든 생성 로직이 하나의 공장에 모이기 때문에, 이 클래스가 너무 복잡해질 위험이 있습니다.
  • 확장성 제한: 새로운 제품을 추가할 때 항상 팩토리 로직 (switch/if 문) 을 수정해야 하므로, 엄밀한 의미의 개방 - 폐쇄 원칙을 완전히 지키기는 어렵습니다.
  • 유지보수: 시간이 지남에 따라 조건문이 늘어날수록 테스트와 디버깅이 어려워질 수 있습니다.

따라서 이 패턴은 주로 진입 장벽을 낮춘 시작점으로 활용되거나, 후속 패턴 (팩토리 메소드 패턴, 추상 팩토리 패턴) 으로 진화하기 위한 중간 단계로서 가치가 큽니다.

태그: java design-pattern simple-factory object-creation creational-pattern

6월 10일 16:40에 게시됨