CHAPTER 04.팩토리 패턴
읽은 책 정리/Head Firstr Design Pattern

CHAPTER 04.팩토리 패턴

팩토리 패턴 이해를 위한 간단한 문제 제안

  • 피자를 만드는 아래와 같은 코드가 있다고 가정해보겠습니다.
public class PizzaStore {
 
	public Pizza orderPizza(String type) {
		Pizza pizza;
 
		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();
		}
 
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();

		return pizza;
	}

}
  • 위 코드는 type을 통해 문자열을 매개변수로 받습니다.
  • 이후 매개변수의 정보에 따라 구현할 피자가 선택되고 피자를 조리하는 코드입니다.
  • 만약 피자가 추가되거나 빼야하는 요구사항이 많은 상황이라면 개발자는 어떻게 해야할 지가 이번 문제 상황입니다.
  • 일단 조리하는 코드는 어떤 피자든 동일하기 때문에 변경할 일이 없기에 분리하여 유지보수 측면의 이점을 가져올 필요가 있어 보입니다.
  • 또한 피자가 어떤 피자로 결정되는지에 대한 if문 코드는 변경이 많이 일어나기 때문에 분리할 필요가 있습니다.

SimpleFactory를 사용해서 문제 해결해보기

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;
	}
}
  • 위의 피자가 선택되고 구현하는 코드를 Factory 메소드 형식으로 분리한것입니다.
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;
	}

}
  • 위에서 생성한 Simple Factory를 사용해서 피자를 선택하고 구현객체를 받는 형식으로 구현한 코드입니다.
  • 이런 방식을 Simple Factory(간단한 팩토리)라고 호칭합니다.
    public class PizzaTestDrive {
     
    	public static void main(String[] args) {
    		SimplePizzaFactory factory = new SimplePizzaFactory();
    		PizzaStore store = new PizzaStore(factory);
    
    		Pizza pizza = store.orderPizza("cheese");
    		System.out.println("We ordered a " + pizza.getName() + "\n");
    		System.out.println(pizza);
     
    		pizza = store.orderPizza("veggie");
    		System.out.println("We ordered a " + pizza.getName() + "\n");
    		System.out.println(pizza);
    	}
    }
  • 테스트는 위와 같이 진행합니다.

실행결과

> Task :PizzaTestDrive.main()
Preparing Cheese Pizza
Baking Cheese Pizza
Cutting Cheese Pizza
Boxing Cheese Pizza
We ordered a Cheese Pizza

---- Cheese Pizza ----
Regular Crust
Marinara Pizza Sauce
Fresh Mozzarella
Parmesan

Preparing Veggie Pizza
Baking Veggie Pizza
Cutting Veggie Pizza
Boxing Veggie Pizza
We ordered a Veggie Pizza

---- Veggie Pizza ----
Crust
Marinara sauce
Shredded mozzarella
Grated parmesan
Diced onion
Sliced mushrooms
Sliced red pepper
Sliced black olives
  • 간단한 팩토리를 팩토리 패턴으로 호칭하는 사람들도 있지만 이는 정확한 표현이 아닙니다.
    • 자주 쓰는 관용구라고 알아두면 좋습니다.
  • 정리하면 위와 같은 구조로 SimpleFactory를 구현하는 예시를 진행해봤습니다.

여러 가게를 운영해야하는 상황에서 문제 해결해보기

  • 이전 SimpleFactory를 사용한 피자 가게가 너무 장사가 잘되서 뉴옥점과 시카고점에 분점을 내기로 하였다.
  • 그런데 두 가게에서 직원들이 피자를 만들다가 bake()과정을 실수 빼먹거나 cut() 과정을 빼먹는등 피자를 만드는 절차를 빼먹게 되었다. 이러한 문제를 해결하기 위해서 제품 클래스(피자)와 생산자 클래스로 나눠서 문제를 해결해본다.
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;
   }
}
public class ChicagoPizzaStore extends PizzaStore {

	Pizza createPizza(String item) {
        	if (item.equals("cheese")) {
            		return new ChicagoStyleCheesePizza();
        	} else if (item.equals("veggie")) {
        	    	return new ChicagoStyleVeggiePizza();
        	} else if (item.equals("clam")) {
        	    	return new ChicagoStyleClamPizza();
        	} else if (item.equals("pepperoni")) {
            		return new ChicagoStylePepperoniPizza();
        	} else return null;
	}
}
  • 먼저 피자를 생산하기 위한 생산자 클래스를 살펴본다.
  • 각각의 분점들은 피자 제조 방법을 공유 받기 위해 PizzStore를 상속받습니다.
  • PizzaStore 안에는 abstract를 사용해서 createPizza를 구현하도록 정의하고 있으며 피자를 구현하는 절차를 미리 정의해놓았습니다.
public abstract class PizzaStore {
 
	abstract Pizza createPizza(String item);
 
	public Pizza orderPizza(String type) {
		Pizza pizza = createPizza(type);
		System.out.println("--- Making a " + pizza.getName() + " ---");
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}
  • PizzaStore 객체에서는 위에서 설명한대로 피자를 구현하기 위한 절차와 서브클래스가 구현해야할 메소드를 미리 정의해 놓았습니다.
💡
abstract는 추상 메소드로 선언하는 정의를 가지고도 있지만 서브클래스가 객체 생성을 책임지도록 한다는 의미를 가지는 방향으로 이해하면 코드 구현에 도움이 된다.
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");
	}
}
public class ChicagoStyleCheesePizza extends Pizza {

   public ChicagoStyleCheesePizza() {
      name = "Chicago Style Deep Dish Cheese Pizza";
      dough = "Extra Thick Crust Dough";
      sauce = "Plum Tomato Sauce";

      toppings.add("Shredded Mozzarella Cheese");
   }

   void cut() {
      System.out.println("Cutting the pizza into square slices");
   }
}
  • 제품 클래스를 살펴보도록 하겠습니다.
  • 여러가지 피자 예시가 있겠지만 각 지점에서 치즈 피자를 만들때 다른 재료를 사용해서 피자를 만드는 상황이라고 가정하겠습니다.
  • 코드 내용을 보면 치즈피자지만 서로 다른 재료를 사용해서 구현하는것을 확인할 수 있습니다.
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 String toString() {
		StringBuffer display = new StringBuffer();
		display.append("---- " + name + " ----\n");
		display.append(dough + "\n");
		display.append(sauce + "\n");
		for (String topping : toppings) {
			display.append(topping + "\n");
		}
		return display.toString();
	}
}
  • 제품 클래스가 상속받는 Pizza 클래스입니다.
  • 주목해야할것은 PizzaStore의 메소드와 orderPizza에서 호출하는 메소드가 일치한다는것입니다.
  • 이렇기 때문에 팩토리 메소드 패턴을 구현할 때는 제품 클래스 생산 클래스의 추상 클래스 선언시 병렬적으로 메소드 이름을 동일할게 가져가야 한다는것을 알 수 있습니다.
    • 생산자 클래스에서 제품을 생산한 이후에 제품의 메소드를 동작 시킬때 제품 클래스와 생산자 클래스 메소드의 이름이 다르다면 오류가 발생하게 될것입니다.
💡
팩토리 패턴은 제품 클래스와 생산 클래스를 나눠서 생각하여 구현하고 이해하는것이 매우 매우 중요한 핵심이라고 생각합니다.
public class PizzaTestDrive {
 
	public static void main(String[] args) {
		PizzaStore nyStore = new NYPizzaStore();
		PizzaStore chicagoStore = new ChicagoPizzaStore();
 
		Pizza pizza = nyStore.orderPizza("cheese");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("cheese");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");
	}
}
  • 테스트 코드입니다.

실행결과

--- Making a NY Style Clam Pizza ---
Prepare NY Style Clam Pizza
Tossing dough...
Adding sauce...
Adding toppings: 
   Grated Reggiano Cheese
   Fresh Clams from Long Island Sound
Bake for 25 minutes at 350
Cut the pizza into diagonal slices
Place pizza in official PizzaStore box
Ethan ordered a NY Style Clam Pizza

--- Making a Chicago Style Clam Pizza ---
Prepare Chicago Style Clam Pizza
Tossing dough...
Adding sauce...
Adding toppings: 
   Shredded Mozzarella Cheese
   Frozen Clams from Chesapeake Bay
Bake for 25 minutes at 350
Cutting the pizza into square slices
Place pizza in official PizzaStore box
Joel ordered a Chicago Style Clam Pizza

팩토리 메소드 패턴의 정의

  • 팩토리 메소드 패턴은 객체를 생성할 때 필요한 인터페이스를 만드는 패턴입니다.
  • 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정합니다.

팩토리 메소드 패턴의 장점

  • 객체 의존성을 줄일수 있습니다.
  • 위와 같이 만약 PizzaStore에 많은 의존을 하고 있고 팩토리 메소든 패턴을 적용하지 않고 객체 인스터스를 직접 만들면 어떤 문제가 발생할까요?
    • 구상 클래스에 의존적이라 OCP원칙을 지키기 어려워질것입니다.

의존성 뒤집기 원칙

  • 구상 클래스에 의존적인것을 방지하기 해결하기 위해 디자인원칙 중 DIP(Dependecy Inversion Principle)을 지켜나가며 개발해야합니다.
  • 의존성 뒤집기란 추상화된 것에 의존하지않고 구상 클래스에 의존하지 않게 만드는것을 의미합니다.
    • 고수준 구성 요소가 저수준 구성 유소에 의존하면 안됩니다.
    • 고수준 구성 요소의 기준은 저수준 구성 요소에 의해 정의되는 행동이 들어있는 구성 요소를 의미합니다.
  • 앞에 피자 예시에 접목하여 설명해보겠습니다.
    • PizzaStore는 고수준 구성 요소라고 할 수있고 피자 클래스는 저수준 구성 요소라고 할 수 있습니다.
    • 이로인해 PizzaStore의 문제점은 PizzaStore가 모든 종류의 피자에 의존한다는 점입니다.
    • 이를 해결하기 위해 추상화 작업을 진행하였고 OrderPizza() 메소드에서 뽑아냈습니다. ⇒ 팩토리 메소드 패턴을 적용하였습니다.
    • 고수준 구성 요소인 PizzaStore와 저수준 구성 요소인 피자 객체 모두가 추상 클래스인 Pizza에 의존합니다.
    • 팩토리 메소드 패턴이 의존성 뒤집기 원칙을 준수하는 유일한 방법은 아니지만 적합한 방법중 하나입니다.

    의존성 뒤집기 적용해보기

  • 필자가 PizzaStore 예제에 의존성 뒤집기를 적용하기 위해 플로우를 정리해보겠습니다.
  1. 피자 가게를 차려야하니까 피자에 대한 절차와 피자 종류를 결정해햐합니다.
  1. 하지만 위에서부터 아래로 내려가면 구상 클래스에 의존적을 코드를 작성할 확률이 높아집니다.
  1. 생각을 뒤집어 보겠습니다.
  1. 가장 밑에 있는 PIzza 클래스를 먼저 생각해보겠습니다.
    1. Pizza 클래스를 추상화할 수 있는 요소를 생각해보겠습니다.
  1. 이후 PizzaStore에서 구상 클래스를 없애도록 팩토리 패턴을 적용합니다.
    1. 여기까지 오면 4번에서 제품군을 구현하고 5번에서 생산군을 추상화한다는것을 알 수 있습니다.
    1. 두 개의 인터페이스는 동일해야합니다.

  • 이러한 법칙을 잘 적용하도록 책에서는 아래와 같은 규칙을 제시합니다.
    • 변수에 구상 클래스의 레퍼런스를 저장하지 말자
      • new 연산자를 사용하면 구상 클래스의 래퍼런스를 사용하게 됩니다.
      • 그러니 팩토리 패턴이나 DIP를 지킬수 있는 디자인 패턴을 사용하여 구상 클래스의 레퍼런스를 변수에 저장하는 일을 사전에 방지합니다.
    • 구상 클래스에서 유도된 클래스를 만들지 말자
      • 구상 클래스에서 유도된 클래스를 만들면 특정 클래스에 의존하게 됩니다.
    • 인터페이스나 추상 클래스처럼 추상화된 것으로부터 클래스를 만들어야합니다.
      • 이미 구현되어 있는 메소드를 오버라이드한다면 베이스 클래스가 제대로 추상화되지 않습니다.
      • 베이스 클래스에서 메소드를 정의할 때는 모든 서브클래스에서 공유할 수 있는 것만 정의해야 합니다.
    • 위 가이드라인은 권고사항이지 반드시 지켜야할 필요는 없습니다.

새로운 문제 제안

  • 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 class ChicagoPizzaIngredientFactory
	implements PizzaIngredientFactory
{

	public Dough createDough() {
		return new ThickCrustDough();
	}

	public Sauce createSauce() {
		return new PlumTomatoSauce();
	}

	public Cheese createCheese() {
		return new MozzarellaCheese();
	}

	public Veggies[] createVeggies() {
		Veggies veggies[] = { new BlackOlives(),
		                      new Spinach(),
		                      new Eggplant() };
		return veggies;
	}

	public Pepperoni createPepperoni() {
		return new SlicedPepperoni();
	}

	public Clams createClam() {
		return new FrozenClams();
	}
}
  • 시카고점은 위와 같은 재료를 사용합니다 뉴옥점과 차이가 있는게 보입니다.
public abstract class PizzaStore {
 
	protected abstract Pizza createPizza(String item);
 
	public Pizza orderPizza(String type) {
		Pizza pizza = createPizza(type);
		System.out.println("--- Making a " + pizza.getName() + " ---");
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}
  • 모든 피자게들은 위와 같은 추상적 선언을 통해 생성됩니다
  • 뉴옥점과 시카고점을 위 추상 클래스를 상속 받아 구현해보겠습니다
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("veggie")) {
 
			pizza = new VeggiePizza(ingredientFactory);
			pizza.setName("New York Style Veggie 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");
 
		} 
		return pizza;
	}
}
public class ChicagoPizzaStore extends PizzaStore {

	protected Pizza createPizza(String item) {
		Pizza pizza = null;
		PizzaIngredientFactory ingredientFactory =
		new ChicagoPizzaIngredientFactory();

		if (item.equals("cheese")) {

			pizza = new CheesePizza(ingredientFactory);
			pizza.setName("Chicago Style Cheese Pizza");

		} else if (item.equals("veggie")) {

			pizza = new VeggiePizza(ingredientFactory);
			pizza.setName("Chicago Style Veggie Pizza");

		} else if (item.equals("clam")) {

			pizza = new ClamPizza(ingredientFactory);
			pizza.setName("Chicago Style Clam Pizza");

		} else if (item.equals("pepperoni")) {

			pizza = new PepperoniPizza(ingredientFactory);
			pizza.setName("Chicago Style Pepperoni Pizza");

		}
		return pizza;
	}
}
  • 둘 다 PizzaStore 객체를 상속받아서 정의 된것을 볼 수 있습니다.
  • 이 부분에 각 지점마다 별도로 사용되는 원재료 팩토리를 넣어서 가맹점이 마진을 위해 별도로 재료를 사용하지 않도록 막은것을 확인할 수 있습니다.
public abstract class Pizza {
	String name;

	Dough dough;
	Sauce sauce;
	Veggies veggies[];
	Cheese cheese;
	Pepperoni pepperoni;
	Clams clam;

//PizzaStore에서 피자를 생성할 수 있도록 추상 메소드를 추가한다.
	abstract void prepare();

	void bake() {
		System.out.println("Bake for 25 minutes at 350");
	}

	void cut() {
		System.out.println("Cutting the pizza into diagonal slices");
	}

	void box() {
		System.out.println("Place pizza in official PizzaStore box");
	}

	void setName(String name) {
		this.name = name;
	}

	String getName() {
		return name;
	}

	public String toString() {
		StringBuffer result = new StringBuffer();
		result.append("---- " + name + " ----\n");
		if (dough != null) {
			result.append(dough);
			result.append("\n");
		}
		if (sauce != null) {
			result.append(sauce);
			result.append("\n");
		}
		if (cheese != null) {
			result.append(cheese);
			result.append("\n");
		}
		if (veggies != null) {
			for (int i = 0; i < veggies.length; i++) {
				result.append(veggies[i]);
				if (i < veggies.length-1) {
					result.append(", ");
				}
			}
			result.append("\n");
		}
		if (clam != null) {
			result.append(clam);
			result.append("\n");
		}
		if (pepperoni != null) {
			result.append(pepperoni);
			result.append("\n");
		}
		return result.toString();
	}
}
  • 피자가게에서 피자를 생성하는 추상 인터페이스를 추가하여 재료 준비를 하는 공통메소드를 지정합니다.
public class ClamPizza extends Pizza {
   PizzaIngredientFactory ingredientFactory;

   public ClamPizza(PizzaIngredientFactory ingredientFactory) {
      this.ingredientFactory = ingredientFactory;
   }

   void prepare() {
      System.out.println("Preparing " + name);
      dough = ingredientFactory.createDough();
      sauce = ingredientFactory.createSauce();
      cheese = ingredientFactory.createCheese();
      clam = ingredientFactory.createClam();
   }
}
  • 마지막으로 Pizza를 상속받는 서브클래스들 전부 prepare() 메소드를 구현해줍니다.
  • prepare() 메소드를 살펴보면 원재료 팩터리에서 재료들을 가져오기 때문에 원재료 변경에 문제가 발생한다면 원재료 팩토리만 관리하면 된다는것을 알 수 있습니다.

추상 팩토리 패턴

  • 위와 같이 제품군(원재료)을 만들 때 사용하는 패턴이 추상 팩토리 패턴입니다.
  • 추상 팩토리 패턴의 정의는 구상 클래스에 의존하지 않고도 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생산하는 인터페이스를 제공하는 패턴입니다. 구상 클래스는 서브 클래스에서 만듭니다.
  • 추상 팩토리 패턴을 사용하면 클라이언트에서 추상 인터페이스 형태로 제품을 공급받을수 있습니다. 이때 클라이언트는 제품이 어떤 제품이 생산되는 전혀 알 필요가 없습니다.
  • 따라서 클라이언트와 팩토리에서 생산되는 의존성을 분리할 수 있습니다.
  • 위의 예제들을 다이어그램으로 표현하여 이해를 돕도록 하겠습니다.
  • 팩토리 패턴 구현시 제품군을 구현할 때 제품군의 추상화를 표현한 다이어그램입니다.

  • 팩토리 패턴 구현시 생산군을 구현할 때 제품군의 원재료군을 추상 팩토리로 생성했을때의 다이어그램입니다. 그림으로 확인하면서 넘어가도록 하겠습니다.

추상 팩토리 패턴과 팩토리 메소드 패턴의 차이점

추상 팩토리 패턴팩토리 메소드 패턴
구현 방법객체를 사용(객체 구성을 통해 구현)클래스를 사용(상속을 통해 구현)
확장 리스크추상화를 정의하여 사용. 제품군을 추가하거나 확대해야 한다면 인터페이스의 변경이 필요클래스를 확장하고 팩토리 메소드를 오버라이드하여 사용. 제품군을 추가하거나 확대시 인터페이스 변경이 필요 없음
클라이언트와 구상혁신 분리 방식팩토리 메소드 패턴과 동일클라이언트는 추상 형식만 알면되고 동작 로직은 서브클래스에서만 처리.
사용하기 좋은 경우클라이언트와 서로 연관된 일련의 제품을 만들어야 할 때클라이언트 코드와 인스턴스를 만들어야 할 구상 클래스를 분리시켜야할 때

예제를 통해 진행했더 팩토리 패턴과 추상 팩토리 패턴 한 눈에 보기

정리

  • 추상 팩토리나 팩토리 패턴이나 핵심은 추상화된 것에 의존하게 만드는것이고 구상 클래스에 의존하지 않게 만드는 것이며 이번 단원에서 강조하는 객체지향 원칙이다.
  • 팩토리를 사용하면 객체 생성을 캡슐화 할 수 있습니다.
  • 간단한 팩토리는 디자인 패턴이 아니라 클라이언트와 구상 클래스를 분리하는 간단한 기법이다.
  • 모든 팩토리 패턴은 애플리캐이션의 구상 클래스 의존성을 줄여줌으로써 느슨한 결합을 도와준다.
  • 추상팩토리 패턴은 구상 클래스에 의존한지 않고 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생성하는 인터페이스를 제공하는것입니다.
    • 구상 클래스는 추상패토리 패턴을 정의하는 서브클래스에서 이루어집니다.
  • 팩토리 메소드 패턴은 객체를 생성할 때 필요한 인터페이스를 만듭니다. 어떤 클래스의 인스턴스를 만들지는 추상팩토리와 동일하게 서브클래스를에서 결정됩니다.
    • 그러나 추상팩토리와 다르게 팩토리 메소드를 사용하면 인스턴스를 만드는 일을 클래스가 위임받아서 할 수 있습니다.

ps.개인적으로 내가 평소에 사용하는 팩토리 방식에서 인터페이스만 추가해서 제약을 걸면 추상 팩토리 패턴이다.