헤드 퍼스트 디자인 패턴 | 에릭 프리먼 | 한빛미디어- 교보ebook

14가지 GoF 필살 패턴!, 경력과 세대를 넘어 오랫동안 객체지향 개발자의 성장을 도와준 디자인 패턴 교과서의 화려한 귀환! 》 2005년부터 디자인 패턴 도서 분야 부동의 1위 》 디자인 패턴의 고

ebook-product.kyobobook.co.kr

객체 인스턴스를 만드는 작업이 항상 공개되어야 하는 것은 아니며, 오히려 모든 것을 공개했다가는 결합 문제가 생길 있다는 사실을 배운다

'new'연산자를 사용한다면 '구상' 떠올려라.

객체 생성 시점에는 구상 클래스에 의존할 밖에 없다.

객체 생성은 추상적인(미완성인) 클래스는 인스턴스화를 없기 때문이다.

 

팩토리메서드

하나의 타입으로 다룰 있다지만, 어떤 구상 클래스로 인스턴스화 할지는 제각각이다.

 

코드를 보면, 런타임 시점에 값을 받아 그에 맞는 구상 클래스 객체를 생성한다.

이런 코드는 변경하거나 확장할 코드를 다시 확인하고 새로운 코드를 추가하거나 기존 코드를 제거해야 한다.

, 코드가 변경에 취약하고, 찾은 변경은 버그의 확률을 높힌다.

 

new 자체는 자바에 필수 연산자다. 당연히 안쓸 없다.

사실 진짜 문제는 "변화"

변화하는 무엇가 때문에 new 조심해서 사용해야 한다.

 

인터페이스에 맞춰서 코딩하는 것은 변화에 다형성으로 대응하기 위함이다. new 연산자는 다형성 대상이 못된다. 무조건 구상 클래스에 의존할 수 밖에 없다. 변경에 닫힌 코드가 된다.

 

팩토리메서드

위 처럼 자주 변하는 부분을 캡슐화해 변하는 부분을 특정했다.

 

객체 생성 부분 캡슐화하기

객체를 생성하는 코드 부분만 뽑아내 객체 생성만을 담당하는 별도 클래스를 만든다.

 

 

객체 생성을 처리하는 클래스를 팩토리라고 부른다

이제 새로운 객체를 직접 만들지 않고 팩토리 메서드를 호출해 처리한다.

이상 어떤 구상 클래스를 인스턴스화 할지 고민하지 않아도 된다.

 

import java.util.ArrayList;
import java.util.List;

public class SimpleFactoryTest {
	static abstract class Pizza{
		String name;
		String dough;
		String sauce;
		List<String> toppings = new ArrayList<>();
		
		public String getName() {
			return name;
		}
		
		public void prepare() {	System.out.println("준비중 ... " + name);	}
		public void bake() {System.out.println("굽는 중 ... "+ name);	}
		public void cut() {	System.out.println("짜르는 중 ... " + name);	}
		public void box() {	System.out.println("포장 중 ... "+ name);	}
		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append("---- " +name+" ----\n")
				.append(dough+"\n")
				.append(sauce+"\n");
			toppings.forEach(topping -> sb.append(topping+"\n"));
			return sb.toString();
		}
	}
	
	static class PepperoniPizza extends Pizza {
		public PepperoniPizza() {
			name = "페퍼로니 피자";
			dough = "크러스트";
			sauce = "마리나라 소스";
			toppings.add("슬라이스 페퍼로니");
			toppings.add("슬라이스 양파");
			toppings.add("모짜렐라 치즈");
		}
	}
	static class ClamPizza extends Pizza {
		public ClamPizza() {
			name = "조개 피자";
			dough = "씬 크러스트";
			sauce = "마늘 소스";
			toppings.add("조개");
			toppings.add("모짜렐라 치즈");
		}
	}
	static class VeggiePizza extends Pizza {
		public VeggiePizza() {
			name = "야채 피자";
			dough = "크러스트";
			sauce = "마리나라 소스";
			toppings.add("슈레드 모짜랄라");
			toppings.add("파마산");
			toppings.add("다신 양파");
			toppings.add("슬라이스 버섯");
			toppings.add("슬라이스 피망");
			toppings.add("슬라이스 블랙 올리브");
		}
	}
	static class CheesePizza extends Pizza {
		public CheesePizza() {
			name = "피즈 피자";
			dough = "크러스트";
			sauce = "마리나라 피자 소스";
			toppings.add("생 모짜렐라");
			toppings.add("파마산");
		}
	}
	
	static 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;
		}
	}
	
	static class PizzaStore{
		SimplePizzaFactory pizzaFactory;

		public PizzaStore(SimplePizzaFactory pizzaFactory) {
			this.pizzaFactory = pizzaFactory;
		}
		
		public Pizza orderPizza(String type) {
			Pizza pizza;
			pizza = pizzaFactory.createPizza(type);
			
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
			
			return pizza;
		}
		
	}
	public static void main(String[] args) {
		SimplePizzaFactory factory = new SimplePizzaFactory();
		PizzaStore store = new PizzaStore(factory);

		Pizza pizza = store.orderPizza("cheese");
		System.out.println("주문하신 피자 " + pizza.getName() + "\n");
		System.out.println(pizza);
 
		pizza = store.orderPizza("veggie");
		System.out.println("주문하신 피자 " + pizza.getName() + "\n");
		System.out.println(pizza);
	}
	
}
준비중 ... 피즈 피자
굽는 중 ... 피즈 피자
짜르는 중 ... 피즈 피자
포장 중 ... 피즈 피자
주문하신 피자 피즈 피자

---- 피즈 피자 ----
크러스트
마리나라 피자 소스
생 모짜렐라
파마산

준비중 ... 야채 피자
굽는 중 ... 야채 피자
짜르는 중 ... 야채 피자
포장 중 ... 야채 피자
주문하신 피자 야채 피자

---- 야채 피자 ----
크러스트
마리나라 소스
슈레드 모짜랄라
파마산
다신 양파
슬라이스 버섯
슬라이스 피망
슬라이스 블랙 올리브

심플 팩토리를 정적 메소드로 정의할 수도 있다. 정적 팩토리라 불르고, 팩토리 인스턴스 생성 없이 바로 객체를 생성할 수 있다는 장점이 있지만, 서브 클래스를 만들어서 객체 생성 메소드의 행동을 변경할 수 없다는 단점이 있다.

 

'간단한 팩토리' 정의

심플 팩토리는 디자인 패턴이라기 보다는 프로그래밍에서 자주 쓰이는 관용구에 가깝다.

하지만, 자주 쓰이는 기법이다.

패턴이 아니라고 해서 중요하지 않은 것은 아니다.

 

 

다양한 팩토리 만들기

import java.util.ArrayList;
import java.util.List;

public class FactoryMethodTest {
	static abstract class Pizza{
		String name;
		String dough;
		String sauce;
		List<String> toppings = new ArrayList<>();
		
		public String getName() {	return name;	}
		
		public void prepare() {
			System.out.println("준비중 ... " + name);
			System.out.println("도우를 돌리는 중 ...");
			System.out.println("소스를 뿌리는 중 ...");
			System.out.println("토핑을 올리는 중 ...");

			toppings.forEach(topping->System.out.println(" "+topping));

		}
		public void bake() {System.out.println("175도에서 25분 간 굽기 ... ");	}
		public void cut() {	System.out.println("피자를 사선으로 짜르기 ... ");}
		public void box() {	System.out.println("피자 상자에 담기 ... ");	}
		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append("---- " +name+" ----\n")
				.append(dough+"\n")
				.append(sauce+"\n");
			toppings.forEach(topping -> sb.append(topping+"\n"));
			return sb.toString();
		}
	}
	
	//추상 팩터리
	abstract static class PizzaStore{
		public Pizza orderPizza(String type) {
			Pizza pizza;
			pizza = createPizza(type);
			
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
			
			return pizza;
		}
		//객체 생성을 서브클래스로 위임
		protected abstract Pizza createPizza(String type);
	}
	
	static class NYPizzaStore extends PizzaStore{
		@Override
		protected Pizza createPizza(String type) {
			Pizza pizza = null;
			if (type.equals("cheese")) {
				pizza = new NYStyleCheesePizza();
			} else if (type.equals("pepperoni")) {
				pizza = new NYStylePepperoniPizza();
			} else if (type.equals("clam")) {
				pizza = new NYStyleClamPizza();
			} else if (type.equals("veggie")) {
				pizza = new NYStyleVeggiePizza();
			}
			return pizza;
		}
		
		static class NYStylePepperoniPizza extends Pizza {
			public NYStylePepperoniPizza() {
				name = "NYStyle 페퍼로니 피자";
				dough = "크러스트";
				sauce = "마리나라 소스";
				toppings.add("슬라이스 페퍼로니");
				toppings.add("슬라이스 양파");
				toppings.add("모짜렐라 치즈");
			}
		}
		static class NYStyleClamPizza extends Pizza {
			public NYStyleClamPizza() {
				name = "NYStyle 조개 피자";
				dough = "씬 크러스트";
				sauce = "마늘 소스";
				toppings.add("조개");
				toppings.add("모짜렐라 치즈");
			}
		}
		static class NYStyleVeggiePizza extends Pizza {
			public NYStyleVeggiePizza() {
				name = "NYStyle 야채 피자";
				dough = "크러스트";
				sauce = "마리나라 소스";
				toppings.add("슈레드 모짜랄라");
				toppings.add("파마산");
				toppings.add("다신 양파");
				toppings.add("슬라이스 버섯");
				toppings.add("슬라이스 피망");
				toppings.add("슬라이스 블랙 올리브");
			}
		}
		static class NYStyleCheesePizza extends Pizza {
			public NYStyleCheesePizza() {
				name = "NYStyle 피즈 피자";
				dough = "크러스트";
				sauce = "마리나라 피자 소스";
				toppings.add("생 모짜렐라");
				toppings.add("파마산");
			}
		}
	}
	
	static class ChicagoPizzaStore extends PizzaStore{
		@Override
		protected Pizza createPizza(String type) {
			Pizza pizza = null;
			if (type.equals("cheese")) {
				pizza = new ChicagoStyleCheesePizza();
			} else if (type.equals("pepperoni")) {
				pizza = new ChicagoStylePepperoniPizza();
			} else if (type.equals("clam")) {
				pizza = new ChicagoStyleClamPizza();
			} else if (type.equals("veggie")) {
				pizza = new ChicagoStyleVeggiePizza();
			}
			return pizza;
		}
		
		static class ChicagoStylePepperoniPizza extends Pizza {
			public ChicagoStylePepperoniPizza() {
				name = "ChicagoStyle 페퍼로니 피자";
				dough = "크러스트";
				sauce = "마리나라 소스";
				toppings.add("슬라이스 페퍼로니");
				toppings.add("슬라이스 양파");
				toppings.add("모짜렐라 치즈");
			}
			@Override
			public void cut() {
				System.out.println("네모난 모양으로 피자 자르기");
			}
		}
		static class ChicagoStyleClamPizza extends Pizza {
			public ChicagoStyleClamPizza() {
				name = "ChicagoStyle 조개 피자";
				dough = "씬 크러스트";
				sauce = "마늘 소스";
				toppings.add("조개");
				toppings.add("모짜렐라 치즈");
			}
		}
		static class ChicagoStyleVeggiePizza extends Pizza {
			public ChicagoStyleVeggiePizza() {
				name = "ChicagoStyle 야채 피자";
				dough = "크러스트";
				sauce = "마리나라 소스";
				toppings.add("슈레드 모짜랄라");
				toppings.add("파마산");
				toppings.add("다신 양파");
				toppings.add("슬라이스 버섯");
				toppings.add("슬라이스 피망");
				toppings.add("슬라이스 블랙 올리브");
			}
		}
		static class ChicagoStyleCheesePizza extends Pizza {
			public ChicagoStyleCheesePizza() {
				name = "ChicagoStyle 피즈 피자";
				dough = "크러스트";
				sauce = "마리나라 피자 소스";
				toppings.add("생 모짜렐라");
				toppings.add("파마산");
			}
		}
	}
	
	static class CaliforniaPizzaStore extends PizzaStore{
		@Override
		protected Pizza createPizza(String type) {
			Pizza pizza = null;
			if (type.equals("cheese")) {
				pizza = new CaliforniaStyleCheesePizza();
			} else if (type.equals("pepperoni")) {
				pizza = new CaliforniaStylePepperoniPizza();
			} else if (type.equals("clam")) {
				pizza = new CaliforniaStyleClamPizza();
			} else if (type.equals("veggie")) {
				pizza = new CaliforniaStyleVeggiePizza();
			}
			return pizza;
		}
		
		static class CaliforniaStylePepperoniPizza extends Pizza {
			public CaliforniaStylePepperoniPizza() {
				name = "CaliforniaStyle 페퍼로니 피자";
				dough = "크러스트";
				sauce = "마리나라 소스";
				toppings.add("슬라이스 페퍼로니");
				toppings.add("슬라이스 양파");
				toppings.add("모짜렐라 치즈");
			}
		}
		static class CaliforniaStyleClamPizza extends Pizza {
			public CaliforniaStyleClamPizza() {
				name = "CaliforniaStyle 조개 피자";
				dough = "씬 크러스트";
				sauce = "마늘 소스";
				toppings.add("조개");
				toppings.add("모짜렐라 치즈");
			}
		}
		static class CaliforniaStyleVeggiePizza extends Pizza {
			public CaliforniaStyleVeggiePizza() {
				name = "CaliforniaStyle 야채 피자";
				dough = "크러스트";
				sauce = "마리나라 소스";
				toppings.add("슈레드 모짜랄라");
				toppings.add("파마산");
				toppings.add("다신 양파");
				toppings.add("슬라이스 버섯");
				toppings.add("슬라이스 피망");
				toppings.add("슬라이스 블랙 올리브");
			}
		}
		static class CaliforniaStyleCheesePizza extends Pizza {
			public CaliforniaStyleCheesePizza() {
				name = "CaliforniaStyle 피즈 피자";
				dough = "크러스트";
				sauce = "마리나라 피자 소스";
				toppings.add("생 모짜렐라");
				toppings.add("파마산");
			}
		}
	}
	
	public static void main(String[] args) {
		//추상화 타입
		PizzaStore nyStore = new NYPizzaStore();
		PizzaStore chicagoPizzaStore = new ChicagoPizzaStore();
		System.out.println(orderPizza(nyStore,"cheese"));
		System.out.println(orderPizza(chicagoPizzaStore,"cheese"));
	}
	
	//추상화된 타입으로 다루기
	static Pizza orderPizza(PizzaStore pizzaStore, String type) {
		return pizzaStore.orderPizza(type);
	}
}
준비중 ... NYStyle 피즈 피자
도우를 돌리는 중 ...
소스를 뿌리는 중 ...
토핑을 올리는 중 ...
 생 모짜렐라
 파마산
175도에서 25분 간 굽기 ... 
피자를 사선으로 짜르기 ... 
피자 상자에 담기 ... 
---- NYStyle 피즈 피자 ----
크러스트
마리나라 피자 소스
생 모짜렐라
파마산

준비중 ... ChicagoStyle 피즈 피자
도우를 돌리는 중 ...
소스를 뿌리는 중 ...
토핑을 올리는 중 ...
 생 모짜렐라
 파마산
175도에서 25분 간 굽기 ... 
피자를 사선으로 짜르기 ... 
피자 상자에 담기 ... 
---- ChicagoStyle 피즈 피자 ----
크러스트
마리나라 피자 소스
생 모짜렐라
파마산

 

팩토리를 추상화하여 추상화 타입으로 만들고 객체 생성 부분을 서브클래스로 위임했다.

팩토리 메서드 패턴의 정의

객체를 생성할 때 필요한 인터페이스를 만든다. 어떤 클래스의 인스턴를 만들지는 서브클래스에서 결정한다.

 

모든 팩토리 패턴은 객체 생성을 캡슐화한다

팩토리 메소드 패턴은 서브클래스에서 어떤 클래스를 만들지 결정함으로써 객체 생성을 캡슐화한다.

 

의존성 뒤집기 원칙

DIP(Dependency Inversion Principle)

디자인 원칙
추상화된 것에 의존하게 만들고 구상 클래스에 의전하지 않게 만든다.

 

의존성 뒤집기 원칙에서는 추상화를 더 강조

고수준 구성 요소가 저수준 구성 요소에 의존하면 안 되며, 항상 추상화에 의존하게 만들어야 한다.

의존성 뒤집기

 

의존성 뒤집기 원칙을 지키는 방법

  • 변수에 구성 클래스의레퍼런스를 저장하지 말기
    new 연산자로 구상클래스에 의존하지 말고, 팩토리를 사용
  • 구상 클래스에서 유도된 클래스를 만들지 말기
    Interface나 abstract class 처럼 추상화된 것으로부터 클래스를 만들어야 한다
  • 베이스 클래스에 이미 구현된 메서드 오버라이드하지 말기
    재정의 시 완전한 추상화가 안된다. 정말 공통적인 것만 베이스 클래스에 있어야한다.

실전에선 위 원칙을 완벽히 준수하기는 힘들다. 하지만, 알고 요령을 피우는 것과 모르고 지키기 않는 것은 차이가 있다.

 

 

 

추상 팩토리 패턴

public class AbstractFactory {
	public static void main(String[] args) {
		PizzaStore nyStore = new NYPizzaStore();
		PizzaStore chicagoStore = new ChicagoPizzaStore();
 
		Pizza pizza = nyStore.orderPizza(PizzaStore.PizzaType.CHEESE);
		System.out.println("Ethan 주문한  " + pizza + "\n");
 
		pizza = chicagoStore.orderPizza(PizzaStore.PizzaType.CHEESE);
		System.out.println("Joel 주문한  " + pizza + "\n");

		pizza = nyStore.orderPizza(PizzaStore.PizzaType.CLAM);
		System.out.println("Ethan 주문한  " + pizza + "\n");
 
		pizza = chicagoStore.orderPizza(PizzaStore.PizzaType.CLAM);
		System.out.println("Joel 주문한  " + pizza + "\n");

		pizza = nyStore.orderPizza(PizzaStore.PizzaType.PEPPERONI);
		System.out.println("Ethan 주문한  " + pizza + "\n");
 
		pizza = chicagoStore.orderPizza(PizzaStore.PizzaType.PEPPERONI);
		System.out.println("Joel 주문한  " + pizza + "\n");

		pizza = nyStore.orderPizza(PizzaStore.PizzaType.VEGGIE);
		System.out.println("Ethan 주문한  " + pizza + "\n");
 
		pizza = chicagoStore.orderPizza(PizzaStore.PizzaType.VEGGIE);
		System.out.println("Joel 주문한  " + pizza + "\n");
	}
	
	/*야채*/
	static interface Veggies{
		String toString();
	}
	static class BlackOlives implements Veggies {
		public String toString() {
			return "검정 올리브";
		}
	}
	static class Eggplant implements Veggies {
		public String toString() {
			return "가지";
		}
	}
	static class Garlic implements Veggies {
		public String toString() {
			return "마늘";
		}
	}
	static class Mushroom implements Veggies {
		public String toString() {
			return "버섯";
		}
	}
	static class Onion implements Veggies {
		public String toString() {
			return "양파";
		}
	}
	static class RedPepper implements Veggies {
		public String toString() {
			return "빨간 피망";
		}
	}
	static class Spinach implements Veggies {
		public String toString() {
			return "Spinach";
		}
	}
	/*치즈*/
	static interface Cheese {
		public String toString();
	}
	static class MozzarellaCheese implements Cheese {
		public String toString() {
			return "슈레드 모짜렐라 치즈";
		}
	}
	static class ParmesanCheese implements Cheese {
		public String toString() {
			return "슈레드 파마산 치즈";
		}
	}
	static class ReggianoCheese implements Cheese {
		public String toString() {
			return "레지아노 치즈";
		}
	}
	/*조개*/
	static interface Clam {
		public String toString();
	}
	static class FreshClams implements Clam {
		public String toString() {
			return "생물 조개";
		}
	}
	static class FrozenClams implements Clam {
		public String toString() {
			return "냉동 조개";
		}
	}
	/*도우*/
	static interface Dough {
		public String toString();
	}
	static class ThickCrustDough implements Dough {
		public String toString() {
			return "두툼하고 빠삭한 스타일 도우";
		}
	}
	static class ThinCrustDough implements Dough {
		public String toString() {
			return "얇고 빠삭한 도우";
		}
	}
	/*소스*/
	static interface Sauce {
		public String toString();
	}
	static class MarinaraSauce implements Sauce {
		public String toString() {
			return "마리나라 소스";
		}
	}
	static class PlumTomatoSauce implements Sauce {
		public String toString() {
			return "이태리 토마토 소스";
		}
	}
	/*페퍼로니*/
	static interface Pepperoni {
		public String toString();
	}
	static class SlicedPepperoni implements Pepperoni {
		public String toString() {
			return "얇게 썰은 페퍼로니";
		}
	}

	/* 핵심  추상 팩토리 정의*/
	static interface PizzaIngredientFactory {
		public Dough createDough();
		public Sauce createSauce();
		public Cheese createCheese();
		public Veggies[] createVeggies();
		public Pepperoni createPepperoni();
		public Clam createClam();
	}
	//시카고는 내륙이라 냉동 조개를 씀
	static 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() {
			return new Veggies[] { new BlackOlives(), new Spinach(), new Eggplant() };
		}
		public Pepperoni createPepperoni() {
			return new SlicedPepperoni();
		}
		public Clam createClam() {
			return new FrozenClams();
		}
	}
	//뉴욕은 연안이라 생물 조개 사용
	static 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() {
			return new Veggies[] { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
		}
		public Pepperoni createPepperoni() {
			return new SlicedPepperoni();
		}
		public Clam createClam() {
			return new FreshClams();
		}
	}
	
	/*피자 군*/
	abstract static class Pizza{
		//팩토리를 사용하여 거의 동일하나 다른 스타일의 피자를 생산할 수 있다.
		PizzaIngredientFactory ingredientFactory;
		String name;
		
		Dough dough;
		Sauce sauce;
		Veggies[] veggies;
		Cheese cheese;
		Pepperoni pepperoni;
		Clam clam;
		
		abstract void prepare();
		
		void bake() {	System.out.println("25분 간 굽습니다.");	}
		void cut() {System.out.println("대각선으로 피자를 썰어줍니다.");}
		void box() {System.out.println("피자박스에 피자를 옯겨 포장합니다.");}
		
		void setName(String name) {
			this.name = name;
		}
		String getName() {
			return name;
		}
		public String toString() {
			StringBuilder result = new StringBuilder();
			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(Veggies veggie : veggies) {
					result.append(veggie+", ");
				}
				result.delete(result.length()-2, result.length());
				result.append("\n");
			}
			if (clam != null) {
				result.append(clam);
				result.append("\n");
			}
			if (pepperoni != null) {
				result.append(pepperoni);
				result.append("\n");
			}
			return result.toString();
		}
	}
	
	static class CheesePizza extends Pizza {
	 
		public CheesePizza(PizzaIngredientFactory ingredientFactory) {
			super.ingredientFactory = ingredientFactory;
		}
	 
		void prepare() {
			System.out.println("준비중..." + name);
			dough = ingredientFactory.createDough();
			sauce = ingredientFactory.createSauce();
			cheese = ingredientFactory.createCheese();
		}
	}
	static class ClamPizza extends Pizza {
		PizzaIngredientFactory ingredientFactory;
	 
		public ClamPizza(PizzaIngredientFactory ingredientFactory) {
			this.ingredientFactory = ingredientFactory;
		}
	 
		void prepare() {
			System.out.println("준비중..." + name);
			dough = ingredientFactory.createDough();
			sauce = ingredientFactory.createSauce();
			cheese = ingredientFactory.createCheese();
			clam = ingredientFactory.createClam();
		}
	}
	static class PepperoniPizza extends Pizza {
		PizzaIngredientFactory ingredientFactory;
	 
		public PepperoniPizza(PizzaIngredientFactory ingredientFactory) {
			this.ingredientFactory = ingredientFactory;
		}
	 
		void prepare() {
			System.out.println("준비중..." + name);
			dough = ingredientFactory.createDough();
			sauce = ingredientFactory.createSauce();
			cheese = ingredientFactory.createCheese();
			veggies = ingredientFactory.createVeggies();
			pepperoni = ingredientFactory.createPepperoni();
		}
	}
	static class VeggiePizza extends Pizza {
	 
		public VeggiePizza(PizzaIngredientFactory ingredientFactory) {
			super.ingredientFactory = ingredientFactory;
		}
	 
		void prepare() {
			System.out.println("준비중..." + name);
			dough = ingredientFactory.createDough();
			sauce = ingredientFactory.createSauce();
			cheese = ingredientFactory.createCheese();
			veggies = ingredientFactory.createVeggies();
		}
	}

	
	
	
	public abstract static class PizzaStore {
		//타입 안정성을 위해 인자를 enum으로 받기
		public enum PizzaType{
			CHEESE, VEGGIE, CLAM, PEPPERONI
		}
		//팩토리 메서드 패턴, 객체 생성을 서브타입에 맡긴다.
		protected abstract Pizza createPizza(PizzaType item);
		
		public Pizza orderPizza(PizzaType type) {
			Pizza pizza = createPizza(type);
			System.out.println("--- Making a " + pizza.getName() + " ---");
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
			return pizza;
		}
	}
	static class ChicagoPizzaStore extends PizzaStore {
		protected Pizza createPizza(PizzaType item) {
			Pizza pizza = null;
			PizzaIngredientFactory ingredientFactory =
			new ChicagoPizzaIngredientFactory();//팩터리
			
			switch (item) {
			case CHEESE:
				pizza = new CheesePizza(ingredientFactory);
				pizza.setName("시카고 스타일 치즈 피자");
				break;
			case VEGGIE:
				pizza = new VeggiePizza(ingredientFactory);
				pizza.setName("시카고 스타일 야채 피자");
				break;
			case CLAM:
				pizza = new ClamPizza(ingredientFactory);
				pizza.setName("시카고 스타일 조개 피자");
				break;
			case PEPPERONI:
				pizza = new PepperoniPizza(ingredientFactory);
				pizza.setName("시카고 스타일 페퍼로니 피자");
				break;
			default:
				throw new IllegalArgumentException();
			}
			return pizza;
		}

	}
	static class NYPizzaStore extends PizzaStore {
		 
		protected Pizza createPizza(PizzaType item) {
			Pizza pizza = null;
			PizzaIngredientFactory ingredientFactory =
			new ChicagoPizzaIngredientFactory();//팩터리
			
			switch (item) {
			case CHEESE:
				pizza = new CheesePizza(ingredientFactory);
				pizza.setName("뉴욕 스타일 치즈 피자");
				break;
			case VEGGIE:
				pizza = new VeggiePizza(ingredientFactory);
				pizza.setName("뉴욕 스타일 야채 피자");
				break;
			case CLAM:
				pizza = new ClamPizza(ingredientFactory);
				pizza.setName("뉴욕 스타일 조개 피자");
				break;
			case PEPPERONI:
				pizza = new PepperoniPizza(ingredientFactory);
				pizza.setName("뉴욕 스타일 페퍼로니 피자");
				break;
			default:
				throw new IllegalArgumentException();
			}
			return pizza;
		}
	}
}
--- Making a 뉴욕 스타일 치즈 피자 ---
준비중...뉴욕 스타일 치즈 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Ethan 주문한  ---- 뉴욕 스타일 치즈 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈


--- Making a 시카고 스타일 치즈 피자 ---
준비중...시카고 스타일 치즈 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Joel 주문한  ---- 시카고 스타일 치즈 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈


--- Making a 뉴욕 스타일 조개 피자 ---
준비중...뉴욕 스타일 조개 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Ethan 주문한  ---- 뉴욕 스타일 조개 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈
냉동 조개


--- Making a 시카고 스타일 조개 피자 ---
준비중...시카고 스타일 조개 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Joel 주문한  ---- 시카고 스타일 조개 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈
냉동 조개


--- Making a 뉴욕 스타일 페퍼로니 피자 ---
준비중...뉴욕 스타일 페퍼로니 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Ethan 주문한  ---- 뉴욕 스타일 페퍼로니 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈
검정 올리브, Spinach, 가지
얇게 썰은 페퍼로니


--- Making a 시카고 스타일 페퍼로니 피자 ---
준비중...시카고 스타일 페퍼로니 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Joel 주문한  ---- 시카고 스타일 페퍼로니 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈
검정 올리브, Spinach, 가지
얇게 썰은 페퍼로니


--- Making a 뉴욕 스타일 야채 피자 ---
준비중...뉴욕 스타일 야채 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Ethan 주문한  ---- 뉴욕 스타일 야채 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈
검정 올리브, Spinach, 가지


--- Making a 시카고 스타일 야채 피자 ---
준비중...시카고 스타일 야채 피자
25분 간 굽습니다.
대각선으로 피자를 썰어줍니다.
피자박스에 피자를 옯겨 포장합니다.
Joel 주문한  ---- 시카고 스타일 야채 피자 ----
두툼하고 빠삭한 스타일 도우
이태리 토마토 소스
슈레드 모짜렐라 치즈
검정 올리브, Spinach, 가지

추상 팩토리 패턴은 구상 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생상하는 인터페이스를 제공한다. 구상 클래스는 서브 클래스에서 만든다.

 

 

 

추상 팩토리 패턴과 팩토리 메소드 패턴 비교

팩토리 메소드는 상속으로 객체를 만든다. 

추상 팩토리는 객체(팩토리) 구성으로 객체를 만든다.

 

팩토리 메소드는 클라이언트와 구상 형식을 분리하는 역할이 목적

추상 팩토리는 일련 연관된 클래스(추상 또는 인터페이스) 군을 하나로 묶은 형식을 제공한다. 다만, 클래스 추가 시 모든 구상 팩토리가 영향을 받는다.

 

핵심 정리

팩토리를 쓰면 객체 생성을 캡슐화할 수 있다.

심플 팩토리는 패턴이 아니라 기법이다.

팩토리 메소드 패턴은 상속으로 객체 생성을 서브 클래스에게 위임한다.

추상 팩토리 패턴은 객체 구성을 활용해 팩토리 인터페이스에 선언한 메소드에서 객체 군을 생성한다.

모든 팩토리는 구상 클래스 의존성을 줄여 느슨한 결합을 도와준다.

 

+ Recent posts