Java/Design Pattern

[헤드퍼스트 디자인 패턴] 4. 팩토리 패턴

noahkim_ 2024. 12. 16. 22:34

에릭 프리먼 님의 "헤드퍼스트 디자인 패턴" 책을 정리한 포스팅 입니다

 

1. new 키워드

  • 특정 구현이 아닌 인터페이스로 프로그래밍해야 함
  • 그러나, 객체 생성시 특정 구현체를 생성해야 함
  • 구현체 클래스에 의존적인 코드가 발생함
    • 변경에만 닫혀있는 코드가 생겨남

 

2. 팩토리 메서드 패턴

  • 객체를 생성할 때, 어떤 클래스의 인스턴스를 만들지를 서브클래스에서 결정하도록 하는 패턴
  • 클라이언트 코드와 구현체 생성 코드를 분리시킴

 

Factory

public class PizzaStore {
    SimplePizzaFactory factory;
    
    public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; }
    	
    public Pizza orderPizza(String type) {
    	Pizza pizza = factory.createPizza(type);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
}
  • 객체 생성을 캡슐화하여 처리하는 클래스
    • 호출자는 구체적인 구현을 알 필요 없음
    • 확장에 열려있고 변경에 닫혀있게 할 수 있음

 

추상 클래스화

public abstract class PizzaStore {	
    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    
    public abstract Pizza createPizza(String type);
}
  • 서브클래스에 피자 생성을 위임함
  • 어떤 종류의 피자를 만들지에 대한것은 어떤 서브클래스를 생성했느냐에 따라 결정됨

 

NYPizzaStore
public class NYPizzaStore extends PizzaStore {
    public Pizza orderPizza(String item) {
        if (item.equals("cheese") return new NYStyleCheesePizza();
        if (item.equals("veggie") return new NYStyleVeggiePizza();
        if (item.equals("clam") return new NYStyleClamPizza();
        if (item.equals("pepperoni") return new NYStylePepperoniPizza();
        
        return null;
    }
}

 

3. 의존성 뒤집기 원칙

  • 고수준 구성요소가 저수준 구성 요소에 의존하면 안됨
    • 추상화된 것에 의존하게 만들기
    • 구현체 클래스에 의존하지 않기

 

의존성 뒤집기 전

public class DependentPizzaStore {
    public Pizza createPizza(String style, String type) {
    	Pizza pizza = null;
        
        if (style.equals("NY")) {
            if (item.equals("cheese") return new NYStyleCheesePizza();
            if (item.equals("veggie") return new NYStyleVeggiePizza();
            if (item.equals("clam") return new NYStyleClamPizza();
    	    if (item.equals("pepperoni") return new NYStylePepperoniPizza();
        } else if (style.equals("Chicago")) {
            if (item.equals("cheese") return new ChicagoStyleCheesePizza();
            if (item.equals("veggie") return new ChicagoStyleVeggiePizza();
            if (item.equals("clam") return new ChicagoStyleClamPizza();
    	    if (item.equals("pepperoni") return new ChicagoStylePepperoniPizza();
        }
        
        return pizza;
    }
}
  • 모든 구현체 클래스를 의존함
    • 구현체 클래스가 변경되면 수정이 필요할 수 있음

 

의존성 뒤집기

  • 추상 팩토리 메서드 패턴을 사용하여 의존성을 역전시킴

 

4. 추상 팩토리 패턴

  • 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생산하는 인터페이스 제공
  • Composition으로 객체 생성

 

Pizza

추상클래스
public abstract class Pizza {
    String name;
    
    Dough dough;
    Sauce sauce;
    Veggies[] veggies;
    Cheese cheese;
    Pepperoni pepperoni;
    Clams clam;
    
    abstract void prepare();
    
    void bake() { // ... }
    void cut() { // ... }
    void box() { // ... }
    
    void setName(String name) { this.name = name; }
    String getName() { return name; }
}
  • 구현체 클래스에 추상계층 추가

 

서브클래스
public class CheesePizza extends Pizza {
    PizzaIngredientFactory pizzaIngredientFactory;
    
    public CheesePizza(PizzaIngredientFactory pizzaIngredientFactory) {
        this.pizzaIngredientFactory = pizzaIngredientFactory; 
    }
    
    void prepare() {
        dough = ingredientFactory.createDough(); 
     	sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
    }
}

 

PIzzaIngredientFactory

인터페이스
public interface PizzaIngredientFactory {    
    Dough createDough(); 
    Sauce createSauce();
    Cheese createCheese();
    Veggies[] createVeggies();
    Pepperoni createPepperoni();
    Clams createClam();
}

 

서브클래스
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    public Dough createDough() { return new ThinCrustDough(); }
    public Sauce createSauce() { return new MarinaraSauce(); }
    public Cheese createCheese() { return new ReggianoCheese(); }
    public Veggie[] createVeggies() { return new[] {new Galic(), new Onion(), new Mushroom(), new RedPepper() }; }
    public Pepperoni createPepperoni() { return new SlicedPepperoni(); }
    public Clams createClam() { return new FreshClams(); }
}
  • 지역별 재료 공장 생성

 

가이드라인

  • 변수에 구현체 클래스의 레퍼런스 저장 X
  • 구현체 클래스에서 유도된 클래스 생성 X
  • 베이스 클래스의 메서드 오버라이딩 X