트렌드브리프
팩토리 메서드 패턴, 객체 생성을 서브클래스에 위임하는 이유

팩토리 메서드 패턴, 객체 생성을 서브클래스에 위임하는 이유

· 게시 · 약 4분 분량

팩토리 메서드 패턴은 객체 생성 결정을 서브클래스에 넘기는 GoF 생성 패턴이다. new로 구체 클래스에 직접 묶이는 코드를 끊는 원리와 Java 예제, 심플 팩토리·추상 팩토리와의 차이를 실무 관점으로 정리했다.

목차
  1. 구체 클래스에 강하게 묶이는 문제를 끊는다
  2. 네 자리로 나눠 코드로 보기
  3. 심플 팩토리·추상 팩토리와 무엇이 다른가
  4. 언제 쓰고, 언제 피하나

팩토리 메서드 패턴은 객체를 만드는 책임을 상위 클래스가 아니라 서브클래스에 넘기는 생성(creational) 디자인 패턴이다. 코드 곳곳에서 new 구체클래스()를 직접 부르는 대신, 무엇을 만들지 정하는 부분만 메서드 하나로 떼어 서브클래스가 재정의하게 한다. GoF(Gang of Four)로 불리는 네 저자의 책 『디자인 패턴』(1994년)이 정리한 생성 패턴 가운데 하나다. 이 글은 객체지향 언어를 다루는 개발자를 대상으로 원형 패턴의 구조와, 이름이 비슷해 자주 뒤섞이는 심플 팩토리·추상 팩토리와의 차이에 초점을 둔다. 언어별 문법 세부나 특정 프레임워크의 구현체는 다루지 않는다.

구체 클래스에 강하게 묶이는 문제를 끊는다

이 패턴이 겨누는 것은 생성 코드가 사용 코드에 섞여 구체 클래스에 강하게 붙어 버리는 상황이다. 알림을 보내는 로직 여러 곳에서 new EmailNotifier()를 직접 호출한다고 하자. 나중에 SMS 알림을 얹으려면 그 호출 지점을 전부 찾아 고쳐야 한다. 생성 지점이 흩어질수록 갈래를 늘릴 때 드는 비용이 커진다.

팩토리 메서드는 이 생성 지점을 createNotifier() 같은 메서드 하나로 모은다. 실제로 무엇을 만들지는 서브클래스가 재정의로 정하고, 쓰는 쪽은 반환 타입인 상위 인터페이스만 알면 된다. 그래서 구체 클래스가 늘어도 사용 코드는 손대지 않는다. 새 종류를 더할 때 기존 코드를 고치지 않고 서브클래스만 추가한다는 점에서 개방-폐쇄 원칙(OCP: 확장에는 열려 있고 수정에는 닫혀 있어야 한다는 설계 원칙)과 맞닿는다.

여기서 핵심은 생성자(Creator)가 객체만 찍어 내는 껍데기가 아니라는 점이다. 생성자에는 대개 제품을 다루는 실제 로직이 함께 들어 있고, 그 로직은 팩토리 메서드가 돌려준 상위 인터페이스만 바라본다. 어떤 구체 제품이 오든 로직은 그대로다. 단순한 if 분기와 갈리는 지점이 바로 여기다.

네 자리로 나눠 코드로 보기

패턴은 네 자리로 나뉜다. 제품 인터페이스(Product), 그 구현체(ConcreteProduct), 팩토리 메서드를 선언하는 생성자(Creator), 그 메서드를 재정의하는 구체 생성자(ConcreteCreator)다. Java로 옮기면 이렇다.

// 제품
interface Button { void render(); }
class WindowsButton implements Button {
 public void render() { /* 윈도우 스타일 */ }
}
class HtmlButton implements Button {
 public void render() { /* HTML 버튼 */ }
}

// 생성자: 팩토리 메서드를 선언만 한다
abstract class Dialog {
 abstract Button createButton(); // 팩토리 메서드

 void renderWindow() {
  Button ok = createButton(); // 무엇이 올지 모른다
  ok.render();
 }
}

// 구체 생성자: 무엇을 만들지 여기서 정한다
class WindowsDialog extends Dialog {
 Button createButton() { return new WindowsButton(); }
}
class WebDialog extends Dialog {
 Button createButton() { return new HtmlButton(); }
}

renderWindow()는 createButton()이 어떤 버튼을 돌려주는지 모른 채 Button 인터페이스로만 다룬다. 그래서 버튼 종류가 늘어도 이 메서드는 그대로다. 실제로 어느 생성자를 쓸지는 프로그램 시작 시점이나 설정에서 한 번 고른다. 운영체제가 윈도우면 WindowsDialog를, 웹이면 WebDialog를 심어 두면 그 뒤 로직은 자기가 무엇을 쥐고 있는지 신경 쓰지 않는다. '무엇을 만들까'라는 결정만 서브클래스로 내려보낸 것이 이 패턴의 골자다.

스탬핑 프레스에 교체 가능한 다이(틀) 세 개가 있고, 끼운 다이에 맞는 제품이 출력되는 선화 일러스트
프레스(생성자)는 그대로, 끼우는 다이(서브클래스)만 바꾸면 나오는 제품이 달라진다.

심플 팩토리·추상 팩토리와 무엇이 다른가

셋은 이름이 겹쳐 헷갈리지만 다루는 층위가 다르다. 심플 팩토리는 엄밀히 패턴이 아니라 관용구다. 메서드 하나가 if나 switch로 어떤 구현체를 만들지 고르는 방식이라 상속이 끼어들지 않는다. 종류가 두세 개로 고정돼 있으면 이걸로 충분할 때가 많다. 팩토리 메서드는 그 결정을 상속으로, 곧 서브클래스 재정의로 옮긴 것이고 한 계열의 제품을 다룬다. 추상 팩토리는 여기서 한 걸음 더 나아가 서로 관련된 여러 계열의 제품을 묶어 만드는 인터페이스를 제공한다. 앞의 예로 치면 버튼 하나가 아니라 버튼·체크박스·입력창을 윈도우용 한 벌, 웹용 한 벌로 통째로 생성하는 식이다. 디자인 패턴 카탈로그 Refactoring.Guru는 많은 설계가 단순한 팩토리 메서드로 출발해 요구가 커지면 추상 팩토리로 옮겨 간다고 설명한다.

언제 쓰고, 언제 피하나

구체 클래스를 미리 확정할 수 없거나, 라이브러리·프레임워크가 사용자에게 생성 지점을 확장할 틈을 열어 줘야 할 때가 제격이다. 반대로 만들 종류가 하나로 고정돼 있고 앞으로 늘어날 계획도 없다면 서브클래스 계층을 세울 이유가 없다. new 한 줄로 끝날 생성을 생성자 클래스 서너 개로 감싸는 것은 유연성이 아니라 과잉 설계다. 한 가지 자주 겹치는 혼동도 짚어 두자. Java의 Calendar.getInstance()처럼 정적 메서드가 객체를 돌려주는 것을 팩토리 메서드라 부르기도 하는데, 이는 정적 팩토리 메서드라는 별개 관용구다. GoF의 팩토리 메서드는 상속과 재정의가 뼈대라 정적 메서드만으로는 성립하지 않는다. 갈래가 실제로 벌어질 조짐이 보일 때 도입해도 늦지 않다.

더 많은 글 보기 RSS 구독