Simple Factory
"new"는 "구상 객체"를 뜻합니다. 즉 new를 사용하는 것은 구상 클래스의 인스턴스를 만드는 것이고 특정 구현에 종속된다는 뜻입니다.
이 코드에서 new를 사용했다고해서 무슨 문제가 있는 것은 아닙니다. 문제는 "변화" 입니다. Cheese 피자 말고 Pepperoni 피자와 Greek 피자도 있다고 한다면 Pizza를 선택할 때 다음과 같이 코드가 바뀌게 됩니다.
pizza = new CheesePizza();
if(type.equals("greek")
prizza = new GreekPizza();
if(type.equals("pepperoni")
pizza = new PepperoniPizza();
새로운 클래스가 추가 될 때마다 코드를 고쳐야 되기 때문에 문제가 생길 수 있습니다. 이런 경우 변화에 닫혀 있다고 합니다. OCP 원칙에 반대되는 형태로군요. 이럴 때 바뀌는 부분을 밖으로 빼내어 Factory 메소드로 만들면 훨씬 보기 좋고 사용하기도 편해집니다.
1232268227.bmpfactory method를 사용하지 않는 경우의 Pizza 클래스입니다.
이 때 분홍색 부분의 코드 때문에 새로운 피자가 추가 될 때 마다 이 클래스는 계속 변경이 되어야 합니다.
이런 변경되는 부분은 따로 빼내는 것은 종종 유용한 경우가 있었습니다.(Strategy Pattern에서 Duck의 행동을 인터페이스로 빼내는 예가 있었습니다.)
이런 부분을 다른 클래스로 빼내고 그 클래스에 factory의 레퍼런스 변수로 팩토리 메소드를 호출하여 Pizza를 받아오는 형식으로 개선 할 수 있겠습니다.
현재의 모습을 클래스 다이어그램으로 보면 다음과 같습니다.
1111263868.bmp
PizzaStore에서 Pizza 인터페이스를 구현한 특정 클래스 들에 종속되고 있습니다. 이것을 개선하기 위해서 이제 분홍색 부분을 SimplePizzaFactory 클래로 옮기고 orderPizza 메소드는 다음과 같이 수정합니다.
소스보기
[#M_ more.. | less.. |
// 팩토리 메소드가 들어있는 클래스
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;
}
}
//바뀐 PizzaStore.java
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
_M#]
이렇게 하면 클래스 다이어그램은 다음과 같이 바뀌게 됩니다.
1097435648.bmp
이렇게 PizzaStore에서 if-else if-else 와 같이 변하는 부분을 따로 클래스로 빼내게 되면 PizzaStore는 새로운 Pizza가 추가 되거나 메뉴에서 사라지더라도 소스코드가 변경될 일은 없습니다. 다만 SimplePizzaFactory의 코드는 계속 변경이 되겠지요.
지금까지 본 것을 하나의 패턴으로 정의 할 수는 없다고 합니다. 하지만 설계와 관련된 기술서를 읽다보면 팩토리 패턴, 팩토리 메소드라는 단어가 나올때 위의 그림을 떠올려 보면 대부분은 쉽게 이해가 될 것입니다.