"new" 는 구상 객체를 뜻한다.
앞에서 배운 것 처럼 구상클래스를 바탕으로 코딩하면 나중에 코드를 수정해야 할 가능성이 높아지고, 유연성이 떨어지게 된다.
Duck duck;
if( picnic) {
duck = new MallardDuck();
} else if( hunting ){
duck = new DecoyDuck();
} else if ( inBathTub){
duck = new RubberDuck();
}
이런 코드는 뭔가를 반영하거나, 확장해야 할때 코드를 다시 확인하고 추가 또는 제거 해야 한다는 것을 뜻한다. 따라서 이런 코드는 관리 및 갱신이 힘들어지고 오류가 생길 가능성이 크다.
애플리케이션에서 구상클래스의 인스턴스를 만드는 부분을 아예 다른쪽으로 분리 시켜야 한다.
앞에서 배웠던 원칙중 "바뀔수 있는 부분을 찾아내서 바뀌지 않는 부분과 분리시켜야 한다." 를 말한다.
간단한 팩토리 예제
public Pizza orderPizza( String type){
Pizza pizza;
if( type.equals("cheese")){
pizza = new CheesePizza();
} else if( type.equals("greek")){
pizza = new GreekPizza();
} else if( type.equals("peperoni")){
pizza = new PeperoniPizza();
}
}
위에 코드 중에 인스턴스 생성 부분을 분리 시키면, 아래와 같이 된다.
public class SimplePizzaFactory{
pubic Pizza createPizza(String type){
Pizza pizza = null;
if( type.equals("cheese")){
pizza = new CheesePizza();
} else if( type.equals("greek")){
pizza = new GreekPizza();
} else if( type.equals("peperoni")){
pizza = new PeperoniPizza();
}
}
}
이런 식으로 바뀌는 부분을 분리시킴으로써 구현을 변경해야 하는 경우에 여기저기 들어가서 고칠필요 없이
팩토리 클래스만 변경하면 된다.
팩토리 메소드 패턴
public abstract class PizzaStore{
public Pizza orderPizza(String item){
Pizza pizza;
pizza = createPizza(item);
...
..
return pizza;
}
abstract Pizza createPizza(String item); //팩토리 기능을 하는 메소드!!
}
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) { // 인스턴스 생성을 책임지는 팩토리 메소드**
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else return null;
}
}
슈퍼 클래스의 orederPizza() 메소드에서 어떤 피자가 어떻게 만들어지는지 모른다.
서브 클래스의 createPizza() 메소드가 결정을 하게 된다. -> 팩토리 메소드의 장점.
abstract Product factoryMethod ( String type )
항상 abstract로 선언해서 서브 클래스에서 객체생성을 책임지도록 해야한다.
* 장점) 팩토리메소드는 객체 생성을 처리하며, 팩토리 메소드를 이용하면
객체를 생성하는 작업을 서브클래스에 캡슐화 시킬수 있다.
이렇게 하면 슈퍼클래스에 있는 클라이언트 코드와 서브클래스에 있는 객체 생성코드를 분리 시킬수 있다.
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList<String> toppings = new ArrayList<String>();
void prepare() {
System.out.println("Prepare " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings: ");
for (String topping : toppings) {
System.out.println(" " + topping);
}
}
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut() {
System.out.println("Cut the pizza into diagonal slices");
}
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
//main
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.prinltn("Ethan ordered a " + pizza.getName() );
출력)
--- Making a NY Style Sauce and Cheese Pizza ---
Prepare NY Style Sauce and Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings:
Grated Reggiano Cheese
Bake for 25 minutes at 350
Cut the pizza into diagonal slices
Place pizza in official PizzaStore box
Ethan ordered a NY Style Sauce and Cheese Pizza
팩토리 메소드 패턴 정의 ) 팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데,
어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만든다.
팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡긴다.
의존성 뒤집기 원칙 ) 추상화된 것에 의존하게 만들어라. 구상 클래스에 의존하도록 만들지 마라.
위에 클래스 다이어그램에서 PizzaStore 와 Pizza , 저수준구성요소인 pizza객체들 관계를 살펴보면
PizzaStore -> Pizza <- pizza ( ex. NYStyleCheesePizza ...etc)
이렇듯 모두 추상클래스인 Pizza에만 의존하는것이 보인다.
팩토리 메소드 패턴으로 인해 의존성 뒤집기 원칙을 적용된 것이다.
PizzaStore에는 Pizza객체만 있고, 저수준구성요소 pizza객체들은
모두 서브클래스에서 결정하기 때문에 이런 관계가 나올수 있는 것이다.
추상 팩토리 패턴
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}
public abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clam;
abstract void prepare();
...
..
}
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
추상 팩토리 패턴 ) 추상 팩토리 패턴에서는 인터페이스를 이용하여 서로 연관된,
또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있습니다.
따라서 클라이언트와 팩토리에서 생산되는 제품을 분리시킬 수 있다.