
// ❌ 나쁜 예: new 연산자에 직접 의존
Duck duck;
if (picnic)
duck = new MallardDuck();
else if (hunting)
duck = new DecoyDuck();
else if (inBathTub)
duck = new RubberDuck();
문제점:
"객체 생성 로직을 별도의 클래스로 분리"
// Factory 클래스
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza;
if (type.equals("cheese"))
pizza = new CheesePizza();
else if (type.equals("pepperoni"))
pizza = new PepperoniPizza();
return pizza;
}
}
// Client 클래스
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza = factory.createPizza(type); // Factory에 위임
pizza.prepare();
pizza.bake();
return pizza;
}
}
장점:
단점:
핵심 아이디어:
public abstract class PizzaStore {
// Template Method - 변하지 않는 알고리즘
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type); // Factory Method 호출
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// Factory Method - 서브클래스가 구현
protected abstract Pizza createPizza(String type);
}
왜 추상 클래스인가?
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String type) {
if (type.equals("cheese"))
return new NYStyleCheesePizza();
else if (type.equals("veggie"))
return new NYStyleVeggiePizza();
return null;
}
}
public class ChicagoPizzaStore extends PizzaStore {
protected Pizza createPizza(String type) {
if (type.equals("cheese"))
return new ChicagoStyleCheesePizza();
else if (type.equals("veggie"))
return new ChicagoStyleVeggiePizza();
return null;
}
}

Creator (추상 클래스)
- orderPizza() ← 공통 알고리즘
- createPizza() ← Factory Method (abstract)
↑
|
┌─────┴─────┐
| |
NYPizzaStore ChicagoPizzaStore
(구체적 Creator들)
관계:
public abstract class Pizza {
String name;
String dough;
String sauce;
void prepare() {
System.out.println("Preparing " + name);
}
void bake() { /* ... */ }
void cut() { /* ... */ }
void box() { /* ... */ }
}
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = "NY Style Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
}
}
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza() {
name = "Chicago Deep Dish Cheese Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
}
void cut() {
System.out.println("Cutting into square slices");
}
}
public class PizzaTestDrive {
public static void main(String[] args) {
// 1. Store 생성 (이때 어떤 Factory를 쓸지 결정)
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
// 2. 주문
Pizza pizza1 = nyStore.orderPizza("cheese");
// → NYStyleCheesePizza 생성
Pizza pizza2 = chicagoStore.orderPizza("cheese");
// → ChicagoStyleCheesePizza 생성
}
}
Factory Method의 한계:
Abstract Factory 해결책:
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
}
왜 Interface인가?
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough(); // NY 스타일
}
public Sauce createSauce() {
return new MarinaraSauce(); // NY 스타일
}
public Cheese createCheese() {
return new ReggianoCheese(); // NY 스타일
}
public Veggies[] createVeggies() {
return new Veggies[] {
new Garlic(), new Onion(), new Mushroom()
};
}
}

public abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
Cheese cheese;
abstract void prepare(); // 재료 준비는 서브클래스가
void bake() { /* ... */ }
void cut() { /* ... */ }
void box() { /* ... */ }
}
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory factory) {
this.ingredientFactory = factory; // Factory 주입
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough(); // Factory에 위임
sauce = ingredientFactory.createSauce(); // Factory에 위임
cheese = ingredientFactory.createCheese(); // Factory에 위임
}
}
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String type) {
Pizza pizza = null;
// 1. Ingredient Factory 생성
PizzaIngredientFactory factory = new NYPizzaIngredientFactory();
// 2. Pizza 생성하면서 Factory 주입
if (type.equals("cheese")) {
pizza = new CheesePizza(factory);
pizza.setName("NY Style Cheese Pizza");
}
return pizza;
}
}
// 1. Store 생성
PizzaStore nyStore = new NYPizzaStore();
// 2. 주문
Pizza pizza = nyStore.orderPizza("cheese");
// 3. orderPizza() 내부:
// - createPizza("cheese") 호출
// - NYPizzaIngredientFactory 생성
// - CheesePizza(factory) 생성
// - pizza.prepare() → Factory로 재료 생성
// - bake(), cut(), box()
| 구분 | Factory Method | Abstract Factory |
|---|---|---|
| 메커니즘 | 상속 (inheritance) | 구성 + 위임 (composition) |
| 생성 대상 | 단일 Product | Product 제품군 (family) |
| 확장성 | 새 Creator 추가 쉬움 | 새 Product 추가 어려움 |
| 관계 | "is-a" (Creator 상속) | "has-a" (Factory 포함) |
| 결정 시점 | 컴파일 타임 (서브클래스 선택) | 런타임 (Factory 객체 전달) |
"추상화에 의존하라. 구체적인 클래스에 의존하지 마라"
// ❌ 나쁜 예: 구체 클래스에 직접 의존
public class DependentPizzaStore {
public Pizza createPizza(String style, String type) {
if (style.equals("NY")) {
if (type.equals("cheese"))
return new NYStyleCheesePizza(); // 구체 클래스
}
}
}
// ✅ 좋은 예: 추상화에 의존
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type); // 추상 메서드
// ...
}
protected abstract Pizza createPizza(String type);
}
Factory Pattern이 DIP를 따르는 방법:
정의: 객체 생성 인터페이스를 정의하되, 서브클래스가 어떤 클래스를 인스턴스화할지 결정하게 함
언제 사용?
정의: 관련되거나 의존적인 객체들의 제품군을 구체 클래스 지정 없이 생성하는 인터페이스 제공
언제 사용?
| Factory Method | Abstract Factory | |
|---|---|---|
| 장점 | • 확장 쉬움 (새 Creator 추가) • 단순함 | • 제품 일관성 보장 • 구체 클래스 격리 • 제품군 교체 쉬움 |
| 단점 | • 클래스 증가 | • 새 제품 타입 추가 어려움 |
// Factory Method: 단일 객체 생성
abstract class DocumentCreator {
abstract Document createDocument();
}
// Abstract Factory: 관련 객체 제품군 생성
interface UIFactory {
Button createButton();
TextField createTextField();
ScrollBar createScrollBar();
}