[디자인 패턴] 팩토리 패턴

maxxyoung·2023년 3월 25일
0

'new'를 사용한 구상 객체 사용의 문제점

  • 코드에서 구상 클래스를 많이 사용하면 새로운 구상 클래스가 추가될 때마다 코드를 고쳐야 하는 문제 발생 -> 변화에 닫혀있는 코드가 됨(OCP 위배)
  • 새로운 구상 형식을 써서 확장해야할 때 어떻게 해서든 다시 열 수 있어야함

간단한 팩토리 메서드

  • 엄밀히 말하면 디자인 패턴은 아님, 관용구에 가까움
  • 객체 생성 부분을 캡슐화 함
    • 객체를 생성을 전담하는 팩토리 객체를 만들어 사용
  • 팩토리를 주입받아 사용하면 new 연산자 대신 팩토리 객체에 있는 create메소드를 써서 구상 클래스의 인스턴스를 사용할 필요가 없음

팩토리 메서드 패턴

팩토리 메소드 패턴 - 팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정하게 만든다. 팩토리 메서드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브 클래스에게 맡긴다.

  • 팩토리 메소드 패턴을 사용하면 구상 클래스에 대한 의존성을 줄일 수 있음
    • DIP 적용(Dependency Inversion Principle)
      • 구상 클래스처럼 구체적인 것이 아닌 추상 클래스나 인터페이스와 같이 추상적인 것에 의존하는 코드(Creator가 Product에 의존)
    • 원칙을 지키는 데 도움이 될만한 가이드라인
      • 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 맙시다
      • 구상 클래스에서 유도된 클래스를 만들지 맙시다
      • 베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드하지 맙시다
        (항상 지키지는 못하나 이를 염두해 둘 수 있음)
      • 만들고 있는 클래스가 바뀔 가능성이 있다면 팩토리 메소드 패턴 같은 기법을 써서 변경될 수 있는 부분을 캡슐화 해야함
public class DependentPizzaStore {
	public Pizza createPizza(String style, String type) {
    	Pizza pizza = null;
        if(style.equals("NY")) {
        	if(type.equals("cheese")){
            	pizza = new NYStyleCheesePizza();
            } else if(type.equals("veggie")) {
            	pizza = new NyStyleVeggiePizza();
            }
        } else if(style.equals("Chicago")) {
        	if(type.equals("cheese")) {
            	pizza = new ChicagoStyleCheesePizza();
            } else if(type.equals("veggie")) {
            	pizza = new ChicagoStyleVeggiePizza();
            }
        }
        pizza.prepare();
        pizza.bake();
        pizza.cur();
        pizza.box();
        return pizza;
    }
}
  • 다음과 같이 의존성이 심하고 객체끼리 강한 결함이 있는 클래스는 팩토리 메서드 패턴을 사용하여 객체를 생성하는 부분을 분리할 수 있음
    • 피자 스토어 클래스 안에서 '뉴욕스타일 치즈 피자', '뉴욕스타일 비건 피자', '시카고 스타일 치즈 피자', '시카고 스타일 비건 피자'의 구상 클래스들을 다 알고 있어야함
    • 해당 클래스로 1000개의 시카고, 치즈 스타일로 객체를 생성했다고 해보자. 시카고에서 뉴욕으로 변경할 경우 모든 클래스 선언을 다 찾아 style Chicago -> NY로 1000번 바꾸어야함
public abstract class PizzaStroe {
	public Pizza orderPizza(String type) {
    	Pizza pizza;
        pizza = createPizza(type);
       
        pizza.prepre();
        pizza.bake();
        pizza.cur();
        pizza.box();
        return pizza;
    }
    protected abstract Pizza createPizza(String type); //피자를 만드는 일은 팩토리 역할을 하는 메소드에서 맡아서 처리
}

public class NYPizzaStore extends PizzaStore {
	Pizza createPizza(String item) {
    	if(item.equals("cheese")){
            	return pizza = new NYStyleCheesePizza();
            } else if(item.equals("veggie")) {
            	return pizza = new NyStyleVeggiePizza();
            }
        }
    }
}

// 시카고 지점 이하 동문

public class PizzaTestDrive {
	public static void main(String[] args) {
    	PizzaStroe nyStore = new NYPizzaStroe();
        pizzaStroe chicagoStore = new ChicagoPizzaStore();
        
        Pizza NYPizza = nyStore.orderPizza("cheese"); //뉴욕 치즈 스타일 피자 반환
        Pizza ChicagoPizza = chicagoStore.orderPizza("cheese"); //시카고 치즈 스타일 피자 반환
    }
}
  • 각각의 피자 구상 클래스들은 Pizza 추상클래스를 상속받아 구현되었음
  • 여기서 뉴욕피자를 시카고 피자로 바꿀 경우 언뜻 보면 구상 객체를 바꾸어야해서 1000번을 고쳐야할 것 같지만, 생성 코드를 한 곳에 모아놓고 체계적으로 관리할 수 있는 디자인을 이용(Spring framework 이용!)하면 빈에 등록하는 객체만 바꾸면 됨!

추상 팩토리 패턴

추상 팩토리 패턴 - 추상 팩토리 패턴에서는 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다.

  • 클라이언트에서 추상 인터페이스를 통해서 일련의 제품들을 공급받을 수 있음
pulbic interface PizzaIngredientFactory {
	public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
}

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    public Dough createDough() {
    	return new ThinCrustDough();
    }
    public Sauce createSauce() {
    	return new MarinaraSauce();
    }
    public Cheese createCheese() {
    	return new ReggianoCheese();
    }
}

public abstract class Pizza {
	String name;
    Dough dough;
    Sauce sause
    
    abstract void prepare();
}

public class CheesePizza extends Pizza {
	PizzaIngredientFactory ingredientFactory;
    
    public CheesePizza(PizzaIngredientFactory ingredientFactory) {
    	this.ingredientFactory = ingredientFactory();
    }
    void prepare() {
    	dough = ingredientFactory.createDough();
        sause = ingredientFactory.createSause();
        cheese = ingredientFactory.createCheese();
    }
}

출처

  • Head First Design Patterns
profile
오직 나만을 위한 글. 틀린 부분 말씀해 주시면 감사드립니다.

0개의 댓글