3. 팩토리 메소드, 추상 팩토리 패턴
디자인 패턴은 무조건 아래 두 글을 선행해야합니다. 짧으니 간단히 읽고 오시면 이해가 쉽습니다.
설명에 사용할 코드는 Java-like Pseudo Code 입니다.
여러 상태에 따른 코드
개발을 하다보면 어떤 상태에 따라 다른 플로를 작성해야할 상황이 발생합니다. 단순히 예/아니오 같은 단일 상태라면 if 문을 사용하도록 배웠고, 다중 상태라면 if-else 혹은 switch 를 사용하도록 배웠습니다. 코드는 간단하게는 로직의 나열이라고 볼 수 있는데요. 우리의 실생활에서도 이처럼 다중 상태에 따라 다양한 작업을 수행하곤 합니다. 결국 모든 실생활도 if-else/switch 로 설명이 가능하다는 의미겠지요.
if-else 에 “의존한” 처리
- 라면 종류별 끓이기라면 종류를 상태로 본다면 명시해준 라면 종류 String type 따라서 다른 라면을 끓입니다. 함수 makeRamen(String type) 은 아래와 같이 두 파트로 나눠볼 수 있습니다.1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13class RamemMaker { 
 public void makeRamen(String type) {
 Water water = new Water(100);
 Ramen ramen;
 heat(water);
 if (type == "볶음") {
 ramen = 볶음라면;
 } else if (type == "국물") {
 ramen = 국물라면;
 }
 water.add(ramen);
 }
 }
- 상태:
 1.1. 라면 종류를 고르고
- 처리: 라면을 끓입니다.
상태와 처리라는 두 책임이 하나의 코드에 모여있군요. 1.상태와 2.처리를 한번 떼어내볼까요.
if-else 를 “분리한” 처리
- 라면 끓이기1 
 2
 3
 4
 5
 6
 7class RamemMaker { 
 public void makeRamen(String type) {
 Water water = new Water(100);
 Ramen ramen = ramenGetter.getRamen(type);
 heat(water);
 water.add(ramen);
 }
- 라면 종류별 생성라면 종류별 생성을 책임지는 상태 함수는 재사용성을 갖게되었고, 상태 책임이 더 명확해 졌습니다. 여기서 함수 getRamen(String type)와 같이 상태에 따라 알맞은 클래스를 만들어서 주입해주는 개념을 팩토리라고 합니다.1 
 2
 3
 4
 5
 6
 7
 8public Ramen getRamen(String type) { 
 if (type == "볶음") {
 return 볶음라면;
 } else if (type == "국물") {
 return 국물라면;
 }
 }
 }
팩토리는 if-else/switch 와 같이 상태에 따라 다른 클래스를 생성 및 주입해주는 개념를 의미합니다.
즉 **어떤 상태(What)**인지에 따라 **어떻게 처리(How)**할지가 다릅니다.
팩토리 메서드 패턴
상태에 따라서 처리를 하기위해 RamemMaker.getRamen 함수 내부에서 if-else 문을 이용하여 분기를 탑니다. 이를 RamemMaker 의 추상 메서드로 만든다면 볶음라면(FriedRamemMaker), 국물라면(StewRamenMaker)에 따라 각각에서 getRamen 를 알맞게 구현하면 됩니다. 기존 if-else 기반 getRamen 을 간단히 팩토리라고 한다면 구현에 따라 달라지는 getRamen 추상 함수를 팩토리 메서드라고 합니다.
- 라면 생성 + 라면 끓이기1 
 2
 3
 4
 5
 6
 7
 8
 9abstract class RamemMaker { 
 protected abstract Ramem getRamen(String type);
 public void makeRamen(String type) {
 Water water = new Water(100);
 Ramen ramen = getRamen(type);
 heat(water);
 water.add(ramen);
 }
 }
- 라면 종류별(볶음) 생성 + 라면 끓이기1 
 2
 3
 4
 5
 6class FriedRamenMaker extends RamenMaker { 
 
 public Ramen getRamen(String type) {
 return new FriedRamen();
 }
 }팩토리 메서드 패턴은 팩토리 개념을 추상함수를 통해 원하는 구현 클래스를 반환하도록 하는것입니다. 
if-else + if-else
팩토리는 단순히 한 상태에 따른 구현(결과물) 클래스를 생성합니다. 라면은 라면 종류라는 한 상태뿐만 아니라 재료라는 추가 상태로도 세분화될 수 있습니다. 두 개의 상태가 생겼군요. 이를 이차원 상태로 보면 아래와 같이 if-else 문 안에 또 하나의 if-else 문을 갖는 구조로 볼 수 있습니다.
- 이차원 상태: 라면 업체 + 라면 종류같은 볶음라면이지만 어떤 재료를 사용했는지에 따라서도 나눌 수 있는것이죠. 일차원 상태분기는 비교적 쉬웠습니다. 이차원 상태를 고려하도록 확장하려면 아래와 같이 될텐데요.1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13if (type == "볶음") { 
 if (ingredient == "해물") {
 return 해물_볶음라면;
 } else if (ingredient == "고기") {
 return 고기_볶음라면;
 }
 } else if (type == "국물") {
 if (ingredient == "해물") {
 return 해물_국물라면;
 } else if (ingredient == "고기") {
 return 고기_국물라면;
 }
 }
- 상태: 
 1.1. 라면 종류를 고르고
 1.2. 재료를 고르고
- 처리: 라면을 끓입니다. 
이번에는 1.상태와 2.처리라는 두 책임뿐 아니라 **두 상태인 1.1.과 1.2.**도 나누어야겠군요.
- 라면 끓이기1 
 2
 3
 4
 5
 6
 7class RamemMaker { 
 public void makeRamen(String type) {
 Water water = new Water(100);
 Ramen ramen = ramenGetter.getRamen(type);
 heat(water);
 water.add(ramen);
 }
- 라면 종류별 생성1 
 2
 3
 4
 5
 6
 7
 8
 9IngredientFactory ingredientFactory = new MeatIngredientFactory(); 
 public Ramen getRamen(String type) {
 if (type == "볶음") {
 return new FriedRamen(ingredientFactory));
 } else if (type == "국물") {
 return new StewRamen(ingredientFactory));
 }
 }
 }
- 라면에 들어갈 재료1 
 2
 3
 4interface IngredientFactory { 
 public Broth getBroth();
 public Flakes getFlakes();
 }
- 라면에 들어갈 고기 재료라면 종류가 함수 내 if-else 로 분기를 탔다면, 라면에 들어갈 재료는 추상 팩토리를 통해 어떤 재료든지 넣을 수 있도록 하였습니다. 전자를 팩토리 후자를 추상 팩토리라고 합니다. 1) 어떤 종류의 라면인지는 팩토리(RamenGetter.getRamen)**에서 선택하고, **추상 팩토리(IngredientFactory)**의 구상 팩토리를 통해 **2) 특정 재료를 넣어주면 최종 라면 결과물이 나옵니다.1 
 2
 3
 4
 5
 6
 7
 8
 9
 10class MeatIngredientFactory implements IngredientFactory { 
 // 고기 육수
 public Broth getBroth() {
 return new MeatBroth();
 }
 // 고기 건더기
 public Flakes getFlakes() {
 return new MeatFlakes();
 }
 }
추상 팩토리 패턴
처음 팩토리를 배울때 “팩토리 메서드 패턴”과 “추상 팩토리 패턴” 두 패턴의 차이를 이해하는데 꽤나 힘들었습니다. 하지만 **팩토리(Factory)-결과물(Product)**의 개념을 잘 이해한다면 어렵지 않습니다.
1) 팩토리 메서드 패턴은 추상 팩토리 메서드를 각 종류에 따라 구현해서 결과물(Product)을 바로 반환(Return)**했다면,
**2) 추상 팩토리 패턴은 추상 팩토리(Interface 혹은 Abstract)에 따라 결과물(Product)을 다르게 **생성(Make & Return)**한다.
- 1) 팩토리 메서드 패턴: 볶음/국물 결정해서 바로 반환1 
 2
 3
 4
 5
 6class FriedRamenMaker extends RamenMaker { 
 ...
 public Ramen getRamen(String type) {
 return new FriedRamen();
 }
 }
- 2) 추상 팩토리 패턴: 볶음/국물 생성을 위한 재료 추상 클래스를 정의1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11class MeatRamenMaker extends RamenMaker { 
 ...
 IngredientFactory ingredientFactory = new MeatIngredientFactory();
 public Ramen getRamen(String type) {
 if (type == "볶음") {
 return new FriedRamen(ingredientFactory));
 } else if (type == "국물") {
 return new StewRamen(ingredientFactory));
 }
 }
 }
팩토리
상태에 따라 그에 맞는 **결과물(Product)**를 반환합니다.
팩토리 메서드 패턴
추상 팩토리 메서드를 각 상태에 따라 구현하여 **결과물(Product)**을 바로 반환합니다.
추상 팩토리 패턴
추상 팩토리(Interface 혹은 Abstract)에 따라 결과물(Product)을 다르게 **생성(Make & Return)**한다.
3. 팩토리 메소드, 추상 팩토리 패턴
https://aaronryu.github.io/2019/02/22/factory-method-and-abstract-factory-pattern/