책너두 (헤드 퍼스트 디자인 패턴) 11일차 (~165p)

요약

  • 객체지향 빵 굽기 : 팩토리 패턴
    • 다양한 팩토리 만들기
    • 서브클래스가 결정하는 것 알아보기
      • 피자 스타일 서브클래스 만들기
      • 팩토리 메소드 선언하기
    • 피자 팩토리 메소드로 피자 주문하기
      • Pizza 클래스 만들기
      • 최첨단 피자 코드 테스트
    • 팩토리 메소드 패턴 살펴보기
      • 생산자 클래스
      • 제품 클래스

메모

다양한 팩토리 만들기

  • 각 지점마다 그 지역의 특성과 입맛을 반영한 다양한 스타일의 피자를 만든다면?
    • 피자 프랜차이즈 사업을 시작한다.
    • SimplePizzaFactory를 삭제하고, NyPizzaFactory, ChicagoPizza, CaliforniaPizzaFactory를 만들어야 함.
    • PizzaStore는 적당한 팩토리를 사용하도록 한다.
NyPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.orderPizza("Veggie");

ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore chicagoStore = new PizzaStore(chicagoFactory);
chicagoStore.orderPizza("Veggie");
  • PizzaStore 를 생성할때, 각 지점의 팩토리를 만들어준다.
  • 만약, 굽는 방식이 달라지거나, 피자를 자르는 걸 까먹거나, 이상한 피자 상자를 쓴다면?
    • 이 문제를 해결하려면 PizzaStore와 피자 제작 코드 전체를 하나로 묶어줘야 함.
    • 유연성을 가져가면서도 피자 가게와 피자 만드는 과정을 하나로 묶는 방법은?

피자 가게 프레임워크 만들기

  • 피자 만드는 일 자체를 전부 PizzaStore 클래스에 진행하면서, 지점의 스타일도 살릴 수 있음.
public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    abstract Pizza createPizza(String type);
}
  • PizzaStore 를 추상 클래스로 바꾼다.
  • createPizza 메소드를 추상 메소드로 만든다. (팩토리 객체 대신 추상 메소드로 대체)

서브클래스가 결정하는 것 알아보기

  • PizzaStore 의 orderPizza 메소드를 통해 이미 주문 시스템은 잘 갖춰져 있고 서브 클래스는 잘 사용만 하면 됨.
  • 서브 클래스는 creatPizza 메소드를 구현해야 함.
  • PizzaSroe 의 orderPizza 메소드는 실제로 어떤 구상 클래스에서 작업이 처리되는지 알 수 없음.
    • 즉, PizzaStore 와 Pizza는 서로 완전히 분리됨.
    • orderPizza 에서 createPizza 메소드가 호출되면 Pizza 서브 클래스가 그 호출을 받아서 피자를 만듦.
      • 피자 가게에 따라 다른 종류의 피자가 만들어짐.
      • 즉, 피자의 종류는 어떤 서브클래스를 선택했느냐에 따라 결정됨.

피자 스타일 서브클래스 만들기

public class NYPizzaStore extends PizzaStore {

    Pizza createPizza(String item) {
        if (item.equals("cheese") {
            return new NYStyleCheesePizza();
        } else if (item.equals("veggie")) {
            return new NYStyleVeggiePizza();
        } else if (item.equals("clam")) {
            return new NYStyleClamPizza();
        } else if (item.equals("pepperoni")) {
            return new NYStylePepperoniPizza();
        }
    }
}
  • 각 지점에서 PizzaStore 서브 클래스를 만들어서 지역별 특성에 맞게 createPizza() 메소드만 구현하면 됨.

팩토리 메소드 선언하기

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    protected abstract Pizza createPizza(String type);
}
  • Pizza 인스턴스를 만드는 일은 팩토리 메소드에서 맡아서 처리함.
  • 피자 객체 인스턴스를 만드는 일은 PizzaStore 서브 클래스에 있는 createPizza() 메소드에서 처리함.
  • 팩토리 메소드는 객체 생성을 서브클래스에 캡슐화할 수 있음.
    • 슈퍼클래스에 있는 클라이언트 코드와 서브클래스에 있는 객체 생성 코드를 분리할 수 있음.

abstarct Product factoryMethod(String type)

  • 팩토리 메소드를 추상 메소드로 선언해서 서브클래스가 객체 생성을 책임지도록 함.
  • 팩토리 메소드는 특정 객체를 리턴하며, 그 객체는 보통 슈퍼클래스가 정의한 메소드 내에서 쓰임.
  • 팩토리 메소드는 클라이언트에서 실제로 생성되는 구상 객체가 무엇인지 알 수 없게 만드는 역할도 함.
  • 팩토리 메소드를 만들 때 매개변수로 만들 객체 종류를 선택할 수도 있음.

피자 팩토리 메소드로 피자 주문하기

  • 뉴욕 스타일 피자와 시카고 스타일 피자를 주문한다.
  1. NyPizzaStore 가 필요함.
PizzaStore nyPizzaStore = new NYPizzaStore();
  1. 주문을 받을 수 있음.
nyPizzaStore.orderPizza("cheese");
  1. orderPizza() 메소드에서 createPizza() 메소드를 호출함.
Pizza pizza = createPizza("cheese");
  • 이 경우, 뉴욕 스타일 치즈 피자가 만들어짐.
  1. 아직 준비되지 않은 피자를 받았으니, 피자 만드는 작업을 마무리 한다.
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
  • 피자가 어떤 구상 클래스의 객체인지 전혀 알지 못함.

Pizza 클래스 만들기

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

    void prepare() {
        System.out.println("준비 중: " + name);
        System.out.println("도우를 돌리는 중...";
        System.out.println("소스를 뿌리는 중...";
        System.out.println("토핑을 올리는 중...";
        for (String topping : toppings) {
            System.out.println(" " + topping);
        }
    }

    void bake() {
        System.out.println("175도에서 25분 간 굽기");
    }

    void cut() {
        System.out.println("피자를 사선으로 자르기");
    }

    void box() {
        System.out.println("상자에 피자 담기");
    }

    public String getName() {
        return name;
    }
}
  • 추상 클래스인 Pizza 임
public class NYStyleCheesePizza extends Pizza {
    public NYStyleCheesePizza() {
        name = "뉴욕 스타일 소스와 치즈 피자";
        dough = "씬 크러스트 도우";
        sauce = "마리나라 소스";

        toppings.add("2270 22");
    }
}

public class ChicagoStyleCheesePizza extends Pizza {
    public ChicagoStyleCheesePizza() {
        name="시카고 스타일 딥 디쉬 치즈 피자";
        dough = "아주 두꺼운 크러스트 도우";
        sauce = "플럼토마토 소스";

        toppings.add("잘게 조각낸 모짜렐라 치즈");
}

    void cut() {
        System.out.println("4498 ");
    }
}
  • 구상 서브 클래스인 뉴욕 스타일 치즈 피자와 시카고 스타일 치즈 피자임.

최첨단 피자 코드 테스트

public class PizzaTestDrive {
    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();

        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("+" + pizza.getName() + "\n");

        pizza chicagoStore.orderPizza("cheese");
        System.out.println("0" + pizza.getName() + "\n");
    }
}

팩토리 메소드 패턴 살펴보기

  • 모든 팩토리 패턴은 객체 생성을 캡슐화 함.
    • 팩토리 메소드 패턴은 서브클래스에서 어떤 클래스를 만들지 결정함으로써 객체 생성을 캡슐화 함.

생산자(Creator) 클래스

  • 추상 생산자 클래스 : PizzaStore
    • 서브 클래스에서 객체를 생산하려고 구현하는 팩토리 메소드를 정의 함.
  • 구상 생산자 클래스(concrete creator) → 팩토리 메소드를 구현함. → 객체를 생산함.

제품(Prodcut) 클래스

  • 생산 대상이 되는 객체임.

댓글

Designed by JB FACTORY