추상팩토리패턴을 공부하기 전에 '심플 팩토리'와 '팩토리 메소드 패턴'을 먼저 공부하면, 이해가 수월할 것이다.
https://flower0.tistory.com/414
팩토리 메소드 패턴
https://flower0.tistory.com/415
추상 팩토리 패턴 Abstract Factory Pattern
[개념] 추상 팩토리 패턴이란?
추상 팩토리 패턴은 서로 연관되거나 의존적인 객체들의 조합(family)을 만드는 인터페이스를 제공하는 방법이다.
[예시]
피자 만드는 것을 (또ㅜ.ㅜ) 예로 들어보자.
팩토리 메소드 패턴까지 설명하자면, PizzaStore가 있고 그걸 상속받는 서브 클래스 (NYPizzaStore, ChicagoPizzaStore)가 있다.
여기서 뉴욕가게와 시카고가게에서 Pizza(객체)를 만들 때 사용되는 재료들은 중복된다.
세부 디테일이 다를 수 있지만 두 가게 모두 도우, 소스, 토핑, 치즈가 필요하다.
이러한 부분을 묶어서 팩토리화하는 것이 추상팩토리패턴이다.
즉 팩토리를 이용하여 피자에서 쓰이는 재료를 만드는 것이다. 만들어지는 구체적인 재료들은 어떤 팩토리를 쓰는지에 따라 달리질 것이다. 도우라고 하더라도 두꺼운 도우일지, 씬일지는 팩토리에 의해 결정된다는 말이다.
NYPizzaIngredientFactory의 createThough()를 호출할 경우, 씬 도우를 반환할 것이고,
ChicagoPizzaIngreidentFactory의 createThough()를 호출할 경우, 두꺼운 도우를 반환할 것이다.
피자를 사용하는 client 입장(PizzaStore)에서는 반환하는 도우의 디테일은 알 필요가 없다. 단지 도우라는 재료(수퍼클래스의 Dough)로만 알고 있으면 되는 것이다.
-> 만들어지는 재료들은 어떤 팩토리를 쓰는지에 따라 달라질 것이고, 피자 클래스에서는 전혀 신경쓸 필요가 없다.
이렇게 함으로써 서로 다른 상황별로 적당한 제품을 생산할 수 있는 다양한 팩토리를 구현할 수 있다.
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Pepperoni createPepperoni();
}
PizzaIngredientFactory 인터페이스에 각 재료별 생성 메소드를 정의한다.
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Pepperoni createPepperoni() {
return new SlicePepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}
client단 구현 - 피자 가게
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
// 뉴욕 피자 가게에는 뉴욕 피자 원재료 공장을 전달해줘야 한다.
// 뉴욕풍 피자를 만들기 위한 재료는 이 공장에서 공급된다.
if( item.equals("cheese") ) {
pizza = new CheesePizza(ingredientFactory);
// 피자 재료를 위해 쓸 팩토리를 각 피자 객체에 전달해준다.
pizza.setName("New York Style Cheese Pizza");
} else if( item.equals("clam") ) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
} else if( item.equals("pepperoni") ) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
}
// 각 형식의 피자마다 새로운 Pizza인스턴스를 만들고 원재료를 공급 받는데 필요한 팩토리를 지정해정해준다.
return pizza;
}
}
Pizza 클래스 - PizzaIngredientFactory가 필요하다.
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
// 각 피자 클래스에서는 생성자를 통해서 팩토리를 전달 받는다.
// 이 팩토리는 인스턴스 변수에 저장한다.
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
voide prepare() {
System.out.println("Preparing " + name);
doug = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
// 재료가 필요할 때마다 팩토리에 있는 메소드를 호출해서 만들어 온다.
}
}
[장점]추상 팩토리의 장점
구상 클래스에 직접 의존하지 않고도 서로 관련된 객체들로 이루어진 제품군을 만들 수 있다.
구상 형식에 대한 의존을 피하고 추상화를 지향할 수 있다.
[단점] 추상 팩토리의 단점
제품군에 제품을 추가하려면 인터페이스를 바꿔야 한다.
위의 피자 예시로 보면, NYPizzaStore에 불고기피자를 추가하려면 PizzaIngredientFactory를 수정해야 하고, 그것을 상속받은 모든 서브 클래스(NYPizzaIngredientFactory)의 인터페이스도 수정이 필요하다.
일이 매우 커진다.
[공통점] 심플 팩토리, 팩토리 메소드 패턴, 추상 팩토리 패턴의 공통점
결국 모든 팩토리의 공통점은 객체 생성을 캡슐화해서 애플리케이션의 결합을 느슨하게 만들고, 특정 구현에 덜 의존이게 만든다는 것이다.
키워드: 캡슐화, 의존성 줄임, 인터페이스화