2025년 6월 21일 작성

Factory Pattern - 객체 생성 캡슐화하기

factory pattern은 객체 생성 logic을 캡슐화하여 객체 생성의 복잡성을 줄이고, code의 재사용성을 높이는 design pattern입니다.

Simple Factory Pattern : 가장 간단한 방식으로 객체 생성을 캡슐화하기

  • Simple Factory는 design pattern은 아니지만 Factory Pattern의 기본이 되는 개념입니다.

  • Simple Factory는 객체를 생성하는 부분을 캡슐화(encapsulation)합니다.
    • 객체 생성을 처리하는 class를 factory라고 부릅니다.
  • 객체 생성하는 작업을 한 class에 캡슐화(encapsulation)시켜 놓으면, 구현을 변경해야 하는 경우에 factory class 하나만 고치면 됩니다.
    • 객체 생성을 캡슐화(encapsulation)해서 application의 결합을 느슨하게 만들고, 특정 구현에 덜 의존하도록 합니다.
    • 또한 code에서 중복되는 내용을 제거할 수 있습니다.
  • Simple Factory로부터 두 Factory Pattern(Factory Method Pattern, Abstract Factory Pattern)이 파생됩니다.
    • Factory Method Pattern은 상속(inheritance)을 통해서 객체를 생성합니다.
    • Abstract Factory Pattern은 합성(composition)을 통해서 객체를 생성합니다.

Static Factory Method

  • Static Factory Method는 Simple Factory를 static method로 정의하는 기법입니다.

  • 장점 : 객체를 생성하기 위한 method를 실행시키기 위해서 객체의 instance를 만들지 않아도 됩니다.
  • 단점 : sub class를 만들어서 객체 생성 method의 행동을 변경시킬 수 없습니다.

Interface로 개발하기

  • interface(또는 abstract class)를 사용하여 개발하면 system에서 일어나는 여러 변화에 유연하게 대응할 수 있습니다.
  • Simple Factory는 interface에 맞춰서 개발할 수 있게 해줍니다.
Interface로 개발하기 Concrete Class로 개발하기
변경 가능성이 있는 부분이 분리되어 있음 변경 가능성이 있는 부분이 분리되어 있지 않음
다형성 덕분에 어떤 class든 특정 interface만 구현하면 사용할 수 있음 나중에 code를 수정해야 할 가능성이 높아지고 유연성이 떨어짐

Simple Factory Example : Pizza 가게

classDiagram

class PizzaStore {
    orderPizza()
}

class SimplePizzaFactory {
    createPizza()
}

class Pizza {
    prepare()
    bake()
    cut()
    box()
}

class CheesePizza
class VeggiePizza
class ClamPizza
class PepperoniPizza

PizzaStore --> SimplePizzaFactory
SimplePizzaFactory --> Pizza

Pizza <|-- CheesePizza
Pizza <|-- VeggiePizza
Pizza <|-- ClamPizza
Pizza <|-- PepperoniPizza
  • PizzaStore : factory를 사용하는 client입니다.
    • PizzaStore에서 SimplePizzaFactory를 통해 pizza instance를 받게 됩니다.
  • SimplePizzaFactory : pizza 객체를 생성하는 factory입니다.
    • 이 application에서 유일하게 concrete class를 직접 참조하는 부분입니다.
    • create method를 static method로 선언하는 경우도 있습니다.
  • Pizza : factory에서 만들어내는 상품인 pizza입니다.
    • method를 overriding해서 쓸 수 있도록 abstract class로 정의합니다.
  • PepperoniPizza : factory에서 생산하는 제품에 해당하는 concrete class입니다.
    • 각 pizza는 Pizza 상위 형식(class 또는 interface)을 구현해야 합니다.

Main

public class PizzaTestDrive {
 
    public static void main(String[] args) {
        SimplePizzaFactory factory = new SimplePizzaFactory();
        PizzaStore store = new PizzaStore(factory);

        Pizza pizza = store.orderPizza("cheese");
        System.out.println("We ordered a " + pizza.getName() + "\n");
        System.out.println(pizza);
 
        pizza = store.orderPizza("veggie");
        System.out.println("We ordered a " + pizza.getName() + "\n");
        System.out.println(pizza);
    }
}

PizzaStore

public class PizzaStore {
    SimplePizzaFactory factory;
 
    public PizzaStore(SimplePizzaFactory factory) { 
        this.factory = factory;
    }
 
    public Pizza orderPizza(String type) {
        Pizza pizza;
 
        pizza = factory.createPizza(type);
 
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

}

SimplePizzaFactory

public class SimplePizzaFactory {

    public Pizza createPizza(String type) {
        Pizza pizza = null;

        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();
        } else if (type.equals("veggie")) {
            pizza = new VeggiePizza();
        }
        return pizza;
    }
}

Pizza

abstract public class Pizza {
    String name;
    String dough;
    String sauce;
    List<String> toppings = new ArrayList<String>();

    public String getName() {
        return name;
    }

    public void prepare() {
        System.out.println("Preparing " + name);
    }

    public void bake() {
        System.out.println("Baking " + name);
    }

    public void cut() {
        System.out.println("Cutting " + name);
    }

    public void box() {
        System.out.println("Boxing " + name);
    }

    public String toString() {
        // code to display pizza name and ingredients
        StringBuffer display = new StringBuffer();
        display.append("---- " + name + " ----\n");
        display.append(dough + "\n");
        display.append(sauce + "\n");
        for (String topping : toppings) {
            display.append(topping + "\n");
        }
        return display.toString();
    }
}

Pizza의 Concrete Class

public class CheesePizza extends Pizza {
    public CheesePizza() {
        name = "Cheese Pizza";
        dough = "Regular Crust";
        sauce = "Marinara Pizza Sauce";
        toppings.add("Fresh Mozzarella");
        toppings.add("Parmesan");
    }
}
public class VeggiePizza extends Pizza {
    public VeggiePizza() {
        name = "Veggie Pizza";
        dough = "Crust";
        sauce = "Marinara sauce";
        toppings.add("Shredded mozzarella");
        toppings.add("Grated parmesan");
        toppings.add("Diced onion");
        toppings.add("Sliced mushrooms");
        toppings.add("Sliced red pepper");
        toppings.add("Sliced black olives");
    }
}
public class ClamPizza extends Pizza {
    public ClamPizza() {
        name = "Clam Pizza";
        dough = "Thin crust";
        sauce = "White garlic sauce";
        toppings.add("Clams");
        toppings.add("Grated parmesan cheese");
    }
}
public class PepperoniPizza extends Pizza {
    public PepperoniPizza() {
        name = "Pepperoni Pizza";
        dough = "Crust";
        sauce = "Marinara sauce";
        toppings.add("Sliced Pepperoni");
        toppings.add("Sliced Onion");
        toppings.add("Grated parmesan cheese");
    }
}

Reference


목차