본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.

팩토리 메서드 패턴(Factory Method Pattern)은 객체를 만드는 인터페이스는 상위 클래스가 정하고, 어떤 클래스의 객체를 만들지는 하위 클래스가 결정하도록 생성 책임을 위임하는 생성 패턴이다.

팩토리 메서드 패턴은 GoF 디자인 패턴의 생성(Creational) 패턴 가운데 하나로, 가상 생성자(Virtual Constructor) 패턴이라고도 한다. 클라이언트 코드 곳곳에 new 구체클래스() 가 흩어져 있으면, 새 제품 클래스가 추가될 때마다 그 코드를 모두 고쳐야 한다. 팩토리 메서드 패턴은 객체 생성을 한 메서드로 모으고 그 메서드를 하위 클래스가 재정의하게 해서, 클라이언트는 인터페이스(추상 타입)에만 의존하게 만든다.

사용하는 이유

편집 원본 편집
  • 생성 코드와 사용 코드의 분리: 객체를 쓰는 쪽은 어떤 구체 클래스가 만들어지는지 몰라도 된다. 결합도가 낮아진다.
  • 개방-폐쇄 원칙(OCP): 새 제품이 생기면 새 하위 클래스만 더하면 되고, 기존 코드는 고치지 않는다 → 객체지향 설계
  • 의존 역전: 상위 모듈이 구체 클래스가 아니라 추상 타입에 의존한다.
  • 생성 로직의 한곳 관리: 초기화 절차, 캐시, 객체 풀 같은 생성 정책을 한 곳에서 바꾼다.
  • 프레임워크 확장점: 프레임워크는 흐름을 정하고, 어떤 객체를 쓸지는 사용자가 하위 클래스에서 정하게 한다.
참여자 역할
Product 팩토리 메서드가 만드는 객체의 공통 인터페이스(또는 추상 클래스)
ConcreteProduct Product 를 구현한 실제 클래스
Creator 팩토리 메서드를 선언한다. Product 를 쓰는 공통 로직도 여기 둔다
ConcreteCreator 팩토리 메서드를 재정의해 특정 ConcreteProduct 를 만들어 돌려준다
 Creator (abstract)                  Product (interface)
   + anOperation()   ── uses ──▶          △
   + factoryMethod() : Product            │ implements
         △                                │
         │ extends                  ConcreteProduct
 ConcreteCreator                          ▲
   + factoryMethod() ──── new ────────────┘

문제가 있는 코드

편집 원본 편집

주문 메서드 안에서 피자 종류를 보고 직접 객체를 만든다. 새 피자가 생기면 orderPizza() 를 고쳐야 한다.

public class PizzaStore {
    public Pizza orderPizza(String type) {
        Pizza pizza;
        if (type.equals("combination")) {
            pizza = new CombinationPizza();
        } else if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else {
            pizza = new PepperoniPizza();
        }
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

1단계: 생성 코드를 팩토리 클래스로 분리(단순 팩토리)

편집 원본 편집
// Factory 클래스
public class SimplePizzaFactory {
    public Pizza createPizza(String type) {
        if (type.equals("combination")) return new CombinationPizza();
        if (type.equals("cheese"))      return new CheesePizza();
        if (type.equals("pepperoni"))   return new PepperoniPizza();
        throw new IllegalArgumentException("unknown pizza: " + type);
    }
}

// Factory 클래스를 사용하는 코드
public class PizzaStore {
    private final SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza = factory.createPizza(type);   // new 가 사라졌다
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

// 사용
PizzaStore store = new PizzaStore(new SimplePizzaFactory());
Pizza p = store.orderPizza("cheese");
  • PizzaStore 는 이제 구체 피자 클래스를 모른다. 피자 종류가 바뀌어도 팩토리만 고친다.
  • 단순 팩토리(Simple Factory)는 GoF 23개 패턴에 들지 않는 관용구이다. 조건 분기가 팩토리 안으로 옮겨 갔을 뿐 없어지지는 않았다.

2단계: 팩토리 메서드 패턴

편집 원본 편집

지점마다 다른 피자를 만들어야 하면, 생성 메서드를 추상 메서드로 두고 하위 클래스가 구현한다.

// Product
public abstract class Pizza {
    public void prepare() { System.out.println("prepare " + getClass().getSimpleName()); }
    public void bake()    { System.out.println("bake"); }
    public void cut()     { System.out.println("cut"); }
    public void box()     { System.out.println("box"); }
}

// ConcreteProduct
public class SeoulCheesePizza  extends Pizza { }
public class BusanCheesePizza  extends Pizza { }

// Creator
public abstract class PizzaStore {
    public final Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);   // 팩토리 메서드 호출
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    protected abstract Pizza createPizza(String type);   // 팩토리 메서드
}

// ConcreteCreator
public class SeoulPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        if (type.equals("cheese")) return new SeoulCheesePizza();
        throw new IllegalArgumentException(type);
    }
}

public class BusanPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        if (type.equals("cheese")) return new BusanCheesePizza();
        throw new IllegalArgumentException(type);
    }
}

// 클라이언트
PizzaStore store = new BusanPizzaStore();
Pizza pizza = store.orderPizza("cheese");   // BusanCheesePizza 가 만들어진다
  • 주문 절차(orderPizza)는 상위 클래스에 고정되고, 어떤 피자를 만들지는 하위 클래스가 정한다.
  • 새 지점은 PizzaStore 를 상속한 클래스를 하나 더하면 된다. 기존 코드는 그대로다.
  • 상위 클래스가 처리 흐름을 정하고 일부 단계를 하위 클래스에 맡긴다는 점에서 템플릿 메서드 패턴과 함께 쓰이는 경우가 많다.

비슷한 패턴과 비교

편집 원본 편집
구분 단순 팩토리 팩토리 메서드 추상 팩토리
생성 책임 별도 팩토리 클래스의 메서드 하나 하위 클래스가 재정의한 메서드 제품군 전체를 만드는 팩토리 객체
확장 방법 팩토리 메서드 안의 분기 수정 새 하위 클래스(ConcreteCreator) 추가 새 팩토리 구현 클래스 추가
핵심 수단 위임(분기) 상속 객체 구성(composition)
만드는 대상 제품 하나 제품 하나 서로 연관된 제품 여러 개(제품군)
GoF 패턴 여부 아니다(관용구) 생성 패턴 생성 패턴
  • 장점
    • 클라이언트가 구체 클래스에 의존하지 않는다
    • 새 제품을 추가할 때 기존 코드를 고치지 않는다
    • 생성 로직이 한곳에 모여 관리가 쉽다
  • 단점
    • 제품마다 Creator 하위 클래스가 늘어나 클래스 수가 많아진다
    • 단순한 생성까지 패턴을 적용하면 구조만 복잡해진다