ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Factory Pattern
    Java/디자인패턴 2024. 1. 17. 11:10

     

    🤔 인터페이스를 사용한다고 해도 구상 클래스의 인스턴스를 만들어야 하지 않나? 

    Duck duck = new MallardDuck();
    // Duck이라는 인터페이스를 써서 유연하게 만들려고 해도, 그럼에도 구상 클래스의 인스턴스를 만들어야 한다.
    
    Duck duck;
    if (picnic) {
    	duck = new MallardDuck();
    } else if (hunting) {
    	duck = new DecoyDuck();
    } else if (inBathTub) {
    	duck = new RubberDuck();
    }

     

    • 이 코드를 보면 구상 클래스의 인스턴스가 여러 개 있고, 그 인스턴스의 형식은 실행 시 조건에 따라 결정된다. 
    • 변경하거나 확장할 때, 코드를 다시 확인하고 새롭게 추가하거나 기존 코드 제거

    => 관리와 갱신이 어렵다. 
    => 인터페이스에 맞춰서 코딩하면 시스템에서 일어날 수 있는 여러 변화에 대응 가능 (인터페이스만 구현하면 사용 가능, 다형성)
    => 구상 클래스를 많이 사용하면 새로운 구상 클래스 추가될 때마다 코드 고쳐야 함
    => 새로운 구상 형식을 써서 확장할 때에는 어떻게 해서든 다시 열 수 있게 만들어야 한다...! 
     

    Pizza orderPizza(String type) {
    		Pizza pizza;
    		
    // 이 부분이 피자가 바뀔 때마다 코드를 계속 추가하고 삭제해야 하는 것이 문제이다.
    // ( 인스턴스를 만드는 구상 클래스 선택하는 부분 )
    // 이 부분을 따로 빼두자(팩토리)
    		if (type.equals("cheese")) {
    				pizza = new CheezePizza();
    		} 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;
    }

     
    인스턴스를 만드는 구상 클래스를 선택하는 부분 -> 이 부분을 따로 빼내자(피자 종류에 따라 계속 바뀌는 부분이므로)

    public class SimplePizzaFactory {
    
    		public Pizza createPizza(String type) {
    				if (type.equals("cheese")) {
    								pizza = new CheezePizza();
    				} 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;
    		}
    }

     

    • 피자 객체 생성 작업을 팩토리 클래스로 캡슐화 해놓으면 구현을 변경할 때 여기저기 고칠 필요 없이 팩토리 클래스 하나만 고치면 된다. 
    • 피자 객체를 받아서 주문하는 클래스 말고도, 피자를 설명하는 클래스, 가격을 알려주는 클래스 등 다른 방식으로 피자를 처리하는 클래스에도 이 팩토리를 사용할 수 있다. 

     

    🤔 간단한 팩토리?

    • 디자인 패턴이라기보다는, 프로그래밍에서 자주 쓰이는 관용구에 가까움
    • PizzaStore(팩토리 사용하는 클라이언트)
    • SimplePizzaFactory (객체를 생성하는 팩토리, 구상 Pizza 클래스를 직접 참조)
    • Pizza (팩토리에서 만드는 피자) 
      • CheesePizza, VeggiePizza, ClamPizza, PepperoniPizza 등등 
      • 팩토리에서 생산하는 제품에 해당하는 구상 클래스
      • Pizza 인터페이스를 구현하며, 각각은 구상 클래스이다.
    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);
    }

     

    • createPizza : PizzaStore 상속받은 NYStylePizzaStore, ChicagoStylePizzaStore 2가지
      • ex ) NYStylePizzaStore의 createPizza(type)
        • NYStyleCheesePizza()
        • NYStylePepperoniPizza() 
    • orderPizza : 그냥 구현하지 않고 가져다 사용, 만약 못 고치도록 막고 싶다면 final 붙이면 된다

     

    🤔 팩토리 메서드 

    • 객체를 생성할 때 필요한 인터페이스를 만든다.
    • 어떤 클래스의 인스턴스를 만들 지는 서브 클래스에서 결정한다.
    • 즉, 클래스 인스턴스 만드는 일을 서브 클래스에게 맡기게 된다.

     
    🍕 Creator 

    • 추상 클래스, 팩토리 메소드 용 인터페이스를 제공한다. 
    • 인터페이스만 제공, 실제 팩토리 메소드를 구현하고 객체 인스턴스 만드는 일은 서브 클래스에서만 할 수 있음 
    • PizzaStore 추상클래스 상속받는 NyPizzaStore, ChicagoPizzStore 
      • PizzaStore의 orderPizza : pizza = createPizzas(type) 이렇게 호출
      • 사용하는 쪽에서 어느 PizzaStore의 orderPizza호출하냐 따라 NyPizza가 만들어질수도, ChicagoPizza가 만들어질수도 있다! 
      • , 서브 클래스에서 어떤 객체가 생성될지 결정되는 것 ! 

     
     

    🤔 간단한 팩토리와 팩토리 메서드 비교 

     
     
     

    🤔 추상 팩토리 

    구상 클래스에 의존하지 않고도 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생산하는 인터페이스를 제공하고, 구상 클래스는 서브클래스에서 만들어진다. 

     

    • 위의 시나리오에서 원재료들을 생산하는 팩토리도 만들자!
    public interface PizzaIngredientFactory {
        public Dough createDough();
        public Sauce creaetSauce();
        public Cheese createCheese();
        public Veggies[] createVeggies();
        public Pepperoni createPepperoni();
        public Clams createClam();
    }

     

    • 각 스타일의 피자별로 implements PizzaIngredientFactory 해서 사용
    • 각각 createXXX 메소드를 지점별로 알맞게 만든다.
      • ex )  냉동 조개를 쓸 수도 있고, 생 조개 쓸 수도 있고..! 
    • 기존 PizzaClass에서도 prepare()을 추상 메소드로 만든다.

    EX) 🧀🧀 CheesePizza 

    public class CheesePizza extends Pizza {
        PizzaIngredientFactory ingredientFactory;
        
        public CheesePizza(PizzaIngredientFactory ingredientFactory) {
        	this.ingredientFactory = ingredientFactory;
        }
        
        void prepare() {
        	dough = ingredientFactory.createDough();
            sauce = ingredientFactory.createSauce();
            // 등등 재료가 필요할 때마다 팩토리에 있는 메소드 호출하여 만든다
        }
    }

     
    EX) 🧀🧀 NYPizzaStore

    public class NYPizzaStore extends PizzaStore {
    	protected Pizza createPizza(String item){
        	Pizza pizza = null;
            PizzaIntgredientFactory ingredientFactory = new NYPizzaIngredientFactory();
            
            // .. 중략, item별로 치즈 피자 만들기 등등 수행 
        }
    }

     

     

    🤔 팩토리 메소드 패턴(PizzaStore)과 추상 팩토리 패턴(PizzaIngredientFactory) 비교

     

     

     

    🍎 팩토리 메소드 패턴 🍎 🍏 추상 팩토리 패턴 🍏
    어플리케이션을 특정 구현으로부터 분리하기 위해 사용
    🍎 클래스를 사용해서 만든다.
    - 상속을 통해 객체를 만든다. 
    - 클래스를 확장, 팩토리 메소드 오버라이드

    🍎 PizzaStore ~ createPizza() 라는 팩토리 메소드 override

    🍎 NYPizzaStore, ChicagoPizzaStore (상속) 

    🍏 객체 구성을 통해 객체를 만든다. 
    - 필드로 클래스 인스턴스 참조 
    - 제품군을 만드는 추상 형식 제공 
    - 제품이 생산되는 방법은 이 형식의 서브 클래스에서 정의

    🍏 PizzaIngredientFactory
    - createDough, createCheese 등 추상 형식을 제공
    - 구체적으로 어떤 dough, cheese인지는 Ny, Chic(하위)에서 

    🍏 NyPizzaStore의 createPizza()
    PizzaIngredientFactory ingredientFactory = new NyPizzaIngredientFactory 넣어주기 
    🍎 클라이언트 코드와 인스턴스를 만들어야 할 구상 클래스를 분리  🍏  새로운 제품을 추가 시 인터페이스 변경이 일어난다. 
     ex)  현재는  dough, cheese인데 ham 이런거 추가한다면? 
    🍎 클라이언트는 자신이 사용할 추상 형식만 알고 있으면 OK
    서브 클래스에서 구상 형식을 처리 해준다. 
    🍏 팩토리를 사용하려면, 팩토리 인스턴스를 만들고, 추상 형식을 써서 만든 코드에 전달
    🍎 어떤 구상 클래스가 필요할지 미리 알 수 없을 때에도 유용 🍏 클라이언트에서 서로 연관된 일련의 제품을 만들 때 즉, 제품 군을 만들 때 활용 

     

     

     

    'Java > 디자인패턴' 카테고리의 다른 글

    Decorator Pattern  (0) 2024.01.11
    Observer Pattern  (0) 2024.01.04
Designed by Tistory.