1. 간단한 팩토리


팩토리 메서드 패턴과 추상 팩토리 패턴을 알아보기 전에 우선, 팩토리가 무엇인지를 알아야합니다.
팩토리는 객체 생성을 처리하는 클래스를 일컫습니다.

아래 코드에서 객체를 생성하는 부분이 if~ else if 로 하드코딩되어 있는데, 이 부분을 팩토리로 뺄 수 있습니다.

public class PizzaStore {
    
    Pizza orderPizza(String type) {
        Pizza pizza;
        
        // 아래 부분이 구체적인 객체를 생성하는 부분입니다.
        // 객체를 생성하는 부분은 새로운게 추가되거나 없어지는 등 언제든 변할 수 있습니다.
        if (type.equals("cheese")) pizza = new CheesePizza();
        else if (type.equals("greekPizza")) pizza = new GreekPizza();
        else if (type.equals("pepperoni")) pizza = new PepperoniPizza();
        
        // 아래 부분은 변하지 않는 부분으로 볼 수 있습니다.
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

public interface Pizza {
    void prepare(); // 피자 만들 준비
    void bake();    // 피자 굽기 
    void cut();     // 피자 커팅 
    void box();     // 다 만든 피자 박스에 담기 
}

public CheesePizza implements Pizza { ... }
public GreekPizza implements Pizza { ... }
public PepperoniPizza implements Pizza { ... }

 

 

피자 종류가 늘어날 때마다 위에서 else if문을 추가해줘야할까요?
이럴 때, 객체 생성을 처리하는 간단한 팩토리를 만들어 객체 생성을 분리할 수 있습니다.

public class SimplePizzaFactory {

    public Pizza createPizza(String type) {
        Pizza pizza = null;
        
        if (type.equals("cheese")) pizza = new CheesePizza();
        else if (type.equals("greekPizza")) pizza = new GreekPizza();
        else if (type.equals("pepperoni")) pizza = new PepperoniPizza();
        
        return pizza;
    }
}

 

이제 위 팩토리를 사용하면 처음에 봤던 코드는 다음과 같은 모양을 가질 수 있습니다.

public class PizzaStore {

    SimplePizzaFactory factory = new SimplePizzaFactory();
    
    Pizza orderPizza(String type) {
        Pizza pizza = factory.createPizza(type);
        
        // 아래 부분은 변하지 않는 부분으로 볼 수 있습니다.
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

public interface Pizza {
    void prepare(); // 피자 만들 준비
    void bake();    // 피자 굽기 
    void cut();     // 피자 커팅 
    void box();     // 다 만든 피자 박스에 담기 
}

public CheesePizza implements Pizza { ... }
public GreekPizza implements Pizza { ... }
public PepperoniPizza implements Pizza { ... }

2. 팩토리 메서드 패턴


위처럼 객체 생성을 위해 별도의 팩토리 클래스를 만들 수도 있지만, 메서드로도 만들어 줄 수 있습니다.
아래 코드를 보면 `createPizza()` 라는 추상 메서드를 두었고, 이 메서드는 객체 생성을 담당하는 팩토리 메서드입니다.
(팩토리라는 용어가 객체 생성과 동일한 의미라고 이해하시면 됩니다.)

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type); // type에 대한 구체적인 피자 객체를 생성해서 반환한다.
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    
    // 객체 생성을 담당하는 팩토리 메서드를 추상 메서드로 정의한다.
    public abstract Pizza createPizza(String type);
}

public interface Pizza {
    void prepare(); // 피자 만들 준비
    void bake();    // 피자 굽기 
    void cut();     // 피자 커팅 
    void box();     // 다 만든 피자 박스에 담기 
}

public CheesePizza implements Pizza { ... }
public GreekPizza implements Pizza { ... }
public PepperoniPizza implements Pizza { ... }

 

즉, 객체 생성을 서브 클래스에 위임한다는 의미인데요,
이제 PizzaStore를 상속받는 구상(Concrete) PizzaStore 클래스를 살펴보겠습니다.

public class NYPizzaStore extends PizzaStore {

    public Pizza createPizza(String type) {
        Pizza pizza = null;
        if ("cheese".equals(type)) {
            pizza = new CheesePizza();
        } else if ("pepperoni".equals(type)) {
            pizza = new PepperoniPizza();
        }
        return pizza;
    }
}

3. 추상 팩토리 패턴


 

추상 팩토리는 처음에 살펴봤던 팩토리를 추상화한거라고 이해하면 됩니다.
팩토리 메서드가 완전한 하나의 제품을 생성하기 위한 용도라면, 추상 팩토리는 제품군(일련의 재료들)을 생성하기 위한 용도입니다. 

public class NYPizzaStore extends PizzaStore {

    // 피자에는 빵, 도우, 치즈, 소스 등 여러가지 재료들이 있을 겁니다. 
    // 어떤 스타일의 재료들을 사용할지를 정해야 할텐데, 이때 아래의 추상 팩토리가 사용됩니다.
    ProductFactory factory = NYProductFactory();

    public Pizza createPizza(String type) {
        Pizza pizza = null;
        if ("cheese".equals(type)) {
            // Concrete Pizza 객체를 생성하면서 추상 팩토리를 넘깁니다.
            pizza = new CheesePizza(factory);
        } else if ("pepperoni".equals(type)) {
            pizza = new PepperoniPizza(factory);
        }
        return pizza;
    }
}