에릭 프리먼 님의 "헤드퍼스트 디자인 패턴" 책을 정리한 포스팅 입니다
1. 기존 문제: new 키워드
- new 키워드를 통해 직접 객체 생성 시 → 특정 구현체에 강하게 의존하게 됨
- ⚠️ OCP, DIP 위반 → 코드 변경에 취약, 테스트 어려움
2. 팩토리 메서드 패턴
- 객체를 생성을 서브클래스에서 위임하는 패턴
- 클라이언트 코드와 구현체 생성 코드를 분리시킴
추상 클래스화
- 서브클래스에 피자 생성을 위임함
- 어떤 종류의 피자를 만들지에 대한것은 어떤 서브클래스를 생성했느냐에 따라 결정됨
- 클라이언트는 어떤 구현이 생성되는지 몰라도 됨
예제) PizzaStore
더보기
추상클래스
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);
}
서브클래스
public class NYPizzaStore extends PizzaStore {
    public Pizza createPizza(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. 추상 팩토리 패턴
- 연관된 여러 객체(제품군)을 생성해야 할 때 사용
- 제품군 생성을 위한 인터페이스 정의
- 구체 팩토리는 Composition 방식으로 필요한 구성 요소 생성 (내부 구성의 생성 책임을 외부 팩토리에 위임)
예제) Pizza
더보기
추상클래스
public abstract class Pizza {
    String name;
    
    Dough dough;
    Sauce sauce;
    Veggies[] veggies;
    Cheese cheese;
    Pepperoni pepperoni;
    Clams clam;
    
    void setName(String name) { this.name = name; }
    String getName() { return name; }
    
    void bake() { ... }
    void cut()  { ... }
    void box()  { ... }
    abstract void prepare();
}
서브클래스
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();
    }
}
Factory
- 호출자는 구체적인 구현을 알 필요 없음
- 확장에 열려있고 변경에 닫혀있게 할 수 있음
예제) 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(); }
}
4. DIP (의존성 뒤집기 원칙)
- 고수준 모듈(정책 결정)은 저수준 모듈(구현 세부사항)에 의존해선 안되며, 둘다 추상화에 의존해야 함
문제점: DIP 전
- 고수준 클래스가 직접 구체 클래스를 생성
- 새로운 구현체가 생기면 고수준 클래스 수정 필수
- ⚠️ OCP 위반
예제) DependentPizzaStore
더보기
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;
    }
}
해결: DIP 적용
- 추상 팩토리 메서드 패턴을 사용하여 의존성을 역전시킴
예제) PizzaFactory
더보기
추상클래스
public interface PizzaFactory {
    Pizza createPizza(String type);
}
서브클래스
public class NYPizzaFactory implements PizzaFactory {
	@Override
    public Pizza createPizza(String type) {
        if (type.equals("cheese")) return new NYStyleCheesePizza();
        if (type.equals("veggie")) return new NYStyleVeggiePizza();
        if (type.equals("clam")) return new NYStyleClamPizza();
        if (type.equals("pepperoni")) return new NYStylePepperoniPizza();
        return null;
    }
}
'Java > Design Pattern' 카테고리의 다른 글
| [헤드퍼스트 디자인 패턴] 6. 커맨드 패턴 (0) | 2024.12.17 | 
|---|---|
| [헤드퍼스트 디자인 패턴] 5. 싱글턴 패턴 (1) | 2024.12.16 | 
| [헤드퍼스트 디자인 패턴] 3. 데코레이터 패턴 (1) | 2024.12.16 | 
| [헤드퍼스트 디자인 패턴] 2. 옵저버 패턴 (0) | 2024.12.16 | 
| [헤드퍼스트 디자인 패턴] 0. 디자인 패턴이란 (1) | 2024.12.16 |