Computer Theory/Design partterns

팩토리 패턴

JHeaon 2023. 9. 20. 00:08

 

 

팩토리 패턴

간단하게 말하자면 객체를 생성하는 부분을 따로 빼서 처리하는 패턴이다.

 

팩토리 페턴은 2가지가 존재한다. 

  • 추상 팩토리 패턴 : 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성한다.
  • 팩토리 메소드 패턴 : 객체를 생성하기 위한 인터페이스를 정의하는데 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정한다.

 

상황

피자 가게에서 피자의 종류를 고르고 그에 맞는 피자를 만드는 코드를 만든다고 가정한다. 

Pizza orderPizza(String type) {
    Pizza pizza;
    
    if (type.equals("cheese")) {
        pizza = new CheesePizza(); 
    } elseif(type.equals("greek")) {
        pizza = new GreekPizza(); 
    } else if (type.equals("pepperoni") { 
        pizza = new PepperoniPizza();
    }
    
    pizza.prepare(); //피자 준비
    pizza.bake();  // 피자 굽기
    pizza.cut();  //피자 자르기
    pizza.box();  // 피자 상자에 담기
    return pizza;
 }

 

 

여기서 조개피자(Clam Pizza)와 야채피자(Veggie Pizza) 2가지 피자를 메뉴에 추가하기로 했다. 그리고 이참에 별로 안 팔리는 그리스 스타일 피자(Greek Pizza)는 메뉴에서 제외하기로 한다고 한다. 이런 요구 조건을 받아드려 코드를 작성한다면 아래와 같이 작성이 가능하다. 

 

Pizza orderPizza(String type) {
    Pizza pizza;
    
    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(); 
    }
    
    pizza.prepare(); //피자 준비
    pizza.bake();  // 피자 굽기
    pizza.cut();  //피자 자르기
    pizza.box();  // 피자 상자에 담기
    return pizza;
 }

 

하지만 위의 코드는 피자의 종류가 바뀔 때 마다 계속 수정해주어야 하며 또한 타입에 따른 피자를 생성하는 코드는 닫혀 있지 않다. 피자 가게에서 메뉴를 변경하려고 한다면 이 코드를 직접 고쳐야 한다. 

 

해당 코드에서 제일 문제점은 인스턴스를 만드는 구상 클래스를 선택하는 if문 쪽이다. 이 부분 때문에 상황이 변하면 코드를 변경하여야 한다. 따라서 이 바뀌는 부분을 캡슐화하여 처리한다. 

 

따라서 객체를 생성하는 부분을 클래스로 만들어 처리한다. 이 부분을 객체를 생성한다고 해서 팩토리 라고 부른다.

 

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;
    }
}
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;
    }
}

이렇게 객체 생성을 팩토리 클래스로 캡슐화하면 구현 변경시 여러 부분을 고칠 필요 없이 팩토리 클래스 하나만 수정해주면 된다. 

 

상황 2

 

이번엔 피자 프랜차이즈 사업을 시작한다고 가정한다. 각 지점마다 그 지역의 특성과 입맛을 반영한 다양한 스타일의 피자를 만들어야 한다. SimplePizzaFactory를 삭제하고, 3가지 서로 다른 팩토리를 만들어 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 클래스에 진행하면서도 지점의 스타일을 살리기 위해 createPizza()을 다시 PizzaStore의 추상 메소드로 선언하고, 지역별 스타일에 맞는 PizzaStore의 서브 클래스를 생성한다. 

package FactoryPattern.Store;

import FactoryPattern.Pizza.Pizza;

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type); //서브클래스에서 결정하도록 함.

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

        return pizza;
    }

    abstract Pizza createPizza(String type); //팩토리 객체 대신 이 메소드를 사용합니다.
}

이렇게 된다면 피자의 스타일은 각 서브 클래스에서 결정되게 된다. 

 

 

 

public class NYPizzaStore extends PizzaStore{

    @Override	
    public Pizza createPizza(String item){			
        if(item.equals("cheese")) return new NYStyleCheesePizza();		
        if(item.equals("peperoni")) return new NYStylePepperoniPizza();		
        if(item.equals("clam")) return new NYStyleClamPizza();		
        if(item.equals("veggie")) return new NYStyleVeggiePizza();		
        return null;
    } 
} 


public class ChicagoPizzaStore extends PizzaStore{	

    @Override	
    public Pizza createPizza(String item){		
        if(item.equals("cheese")) return new ChicagoStyleCheesePizza();		
        if(item.equals("peper")) return new ChicagoStylePepperoniPizza();		
        if(item.equals("clam")) return new ChicagoStyleClamPizza();		
        if(item.equals("veggie")) return new ChicagoStyleVeggiePizza();	
        return null;
	} 
}
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());		
        
        pizza = chicagoStore.orderPizza("cheese");		
        System.out.println(pizza.getname());	
    } 
}

 

이렇게 함으로써, 기존에 모든 피자 객체를 PizzaStore 클래스 내에서 직접 만들었을 때, 심하게 의존적이었던 PizzaStore에 대한 의존성을 줄일 수 있다.


https://msyu1207.tistory.com/entry/4%EC%9E%A5-%ED%97%A4%EB%93%9C%ED%8D%BC%EC%8A%A4%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4

 

4장. 헤드퍼스트 디자인 패턴 - 팩토리 패턴

안녕하세요 😀 유로띠 입니다 😉 헤드퍼스트 디자인 패턴 TIL (Today I Learned) 3줄 요약 ✏️ 간단한 팩토리 / 팩토리 메소드 / 추상 팩토리 메소드가 있다. ✏️ 의존성 뒤집기! - 추상화된 것에 의

msyu1207.tistory.com

 

'Computer Theory > Design partterns' 카테고리의 다른 글

디자인 패턴과 SOLID 원칙  (0) 2024.05.22
데코레이터 패턴  (0) 2023.09.18
옵저버 패턴  (0) 2023.09.04
전략 패턴  (0) 2023.08.30

'Computer Theory/Design partterns'의 다른글

  • 현재글 팩토리 패턴

관련글