피자 객체 생성 | 간단한 피자 팩토리 |
---|---|
{code:java} //============================================== //피자 가게 운영을 위한 객체생성 //============================================== Pizza orderPizza(){ |
//추상 클래스, 인터페이스로는 직접 인스턴스를 만들 수 없다.
Pizza pizza = new Pizza();
Pizza.prepare();
Pizza.bake();
Pizza.cut();
Pizza.box();
return pizza;
}
{code:java}
//==============================================
//여러 종류 피자를 만들기 위한 수정
//==============================================
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;
}
|
//==============================================
//피자 가게 객체 생성에 캡슐화 적용
//==============================================
Pizza orderPizza(String type){
Pizza pizza;
Pizza.prepare();
Pizza.bake();
Pizza.cut();
Pizza.box();
return pizza;
}
//==============================================
//피자를 만들기 위한 객체생성
//==============================================
public class SimplePizzaFactory{
//클라이언트에서 새로운 객체의 인스턴스를 만들때 호출
public Pizza createPizza (String type){
Pizza pizza = null;
//orderPizza() 메소드에서 뽑아낸 코드(피자의 종류를 결정)
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;
}
}
//==============================================
//팩토리를 이용한 피자 객체를 생성
//==============================================
public class PizzaStore{
//Simplepizzafactory에 대한 레퍼런스를 저장
SimplePizzaFactory factory;
//PizzaStore의 생성자에 팩토리 객체 전달
public Pizza createPizza (SimplePizzaFactory factory){
this.factory = factory;
}
public Pizza orderPizza (String type){
Pizza pizza;
/*orderPizza() 매소드에서는 팩토리를 써서 피자 객체 생성.
new 연산자 대신 팩토리 객체에 있는 create 메소드를 사용. 구상 클래스의 인스턴스를 만들 필요가 없다.*/
pizza = faactory.createPizza(type);
Pizza.prepare();
Pizza.bake();
Pizza.cut();
Pizza.box();
return pizza;
}
}
<피자가게의 클래스 다이어그램>
디자인 패턴에서 '인터페이스를 구현한다' 는 표현이
항상 클래스 선언 부분의 implements 키워드를 써서 인터페이스 구현하는 클래스를 만든다는 것이 아니다.
일반적으로 상위 형식(클래스 or 인터페이스)에 있는 메소드를 구현하는 구상 클래스는 그 상위 형식의 '인터페이스를 구현하는' 클래스라고 생각하면 된다.
*Point : 피자는 PizzaStore 클래스에서 만들고, 분점마다 고유 스타일을 살리자.
![]() ![]() | {code:java} //뉴욕 스타일 피자를 만들기 위한 팩토리 생성 NYPizzaFactory nyFactory = new NYPizzaFactory(); |
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.order("Veggie");
{code:java}
//ChicagoPizzaFactory 객체가 포함되어 있는 PizzaStore 객체를 만듬.
ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore chicagoStore = new PizzaStore(chicagoFactory);
chicagoStore.order("Veggie");
//==============================================
//추상클래스 (PizzaStore) 생성
//==============================================
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
//PizzaStore의 createPizza를 호출
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
//팩토리 객체대신 사용
abstract createPizza(String type);
}
public abstract 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);
}
-코드탐구-
abstract Product factoryMethod(String type)
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
void prepare() {
System.out.println("Preparing " + name);
System.out.println("Tossing dough..");
System.out.println("Adding sauce... ");
System.out.println("Adding toppings: ");
for(int i=0; i< toppings.size(); i++) {
System.out.println(" "+ toppings.get(i));
}
}
void bake() {
System.out.println("Bake for 25 miutes at 350");
}
void cut() {
System.out.println("Cutting 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");
}
}
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza() {
name = "Chicago Style Sauce and 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 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 pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}
<다른 형태의 프레임 워크로 바라보기(두 클래스의 연관성 찾기)>
제품 클래스 | 생산자 클래스 |
---|---|
![]() | ![]() |
.Creator 추상 클래스에서 팩토리 메소드를 위한 인터페이스를 제공한다
.서브클래스에서 실제 팩토리 메소드를 구현하고 제품(객체 인스턴스)을 만들어내는 일을 수행한다
.사용하는 서브클래스에 따라 생산되는 객체 인스턴스가 결정된다
사부와 제자
사부 : '팩토리'를 썼을 때 어떤 장점이 있느냐~?
제자 :
1. 객체 생성코드를 전부 한 객체 또는 메소드에 집어 넣으면 코드에서 중복되는 내용을 제거할 수 있습니다.
2. 클라이언트 입장에서는 객체 인스턴스를 만들 때 필요한 구상 클래스가 아닌 인터페이스만 필요하게 됩니다.
3. 인터페이스를 바탕으로 프로그래밍을 할수 있어, 유연성과 확장성이 뛰어난 코드를 만들 수 있습니다.
public class DependentPizzaStore {
public Pizza createPizza(String style, String type) {
Pizza pizza = null;
//뉴욕풍 피자를 처리하는 부분
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
}
else if (type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
}
else if (type.equals("clam")) {
pizza = new NYStyleClamPizza();
}
else if (type.equals("pepperoni")) {
pizza = new NYStylePepperoniPizza();
}
//시카고풍 피자를 처리하는 부분
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
}
else if (type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
}
else if (type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
}
else if (type.equals("pepperoni")) {
pizza = new ChicagoStylePepperoniPizza();
}
} else {
System.out.println("Error: invalid type of pizza");
return null;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
-고수준 구성요소가 저수준 구성요소에 의존하면 안된다.(고수준, 저수준 모듈이 둘 다 하나의 추상 클래스에 의존하는 것)
-의존성 뒤집기 원칙에 의하면, 구상 클래스처럼 구체적인 것이 아닌 '추상 클래스나 인터페이스와 같이 추상적인 것에 의존하는 코드'를 만들어야 한다.
-심하게 의존적인 PizzaStore()의 가장 큰 문제점은 모든 종류의 피자에 의존한다는 점이다.
-팩토리 메소드 패턴을 적용하고 나면 고수준 구성요소인 PizzaStore와 저수준 구성요소인 파자 객체들이 모두 추상 클래스인 Pizza에 의존한다.
-팩토리 메소드 패턴은 의존성 뒤집기 원칙을 준수하기 위해 쓸 수 있는 가장 적합한 방법 가운데 하나이다.
1. 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 맙시다.
2. 구상 클래스에서 유도된 클래스를 만들지 맙시다.
3. 베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드 하지 맙시다.
위 가이드 라인은 항상 지켜야 하는 규칙이 아니라, 개발자가 지향해야 하는 규칙이다.
-원재료를 생산할 팩토리를 위한 인터페이스 정의
public interface PizzaIngredientFactory {
//각 재료별로 생성 메소드 정의.
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
-모든 팩토리 인스턴스에서 공통적으로 사용하는 부분이 있다면 인터페이스가 아닌 추상클래스로 만들어도 된다.
-이렇게 해봅시다.
1. 지역별로 팩토리를 만든다. 각 생성 메소드를 구현하는 PizzaIngredientFactory 클래스를 만들어야 한다.
2. Reggianocheese, RedPeppers, ThickCrustDough와 같이 팩토리에서 사용할 원재료 클래스들을 구현한다. 상황에 따라 서로 다른 지역에서 같은 재료 클래스를 쓸 수도 있다.
3. 새로 만든 원재료 공장을 PizzaStore 코드에서 사용하도록 함으로써 모든 것을 하나로 묶어줘야 된다.
//========================================================================
//모든 재료 공장에서 구현해야 하는 인터페이스를 구현
//========================================================================
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();
}
}
-코드탐구
sauce = ingredientFactory.createSauce();
-추상 팩토리(Abstract Factory)라고 부르는 새로운 형식의 팩토리 도입한다.
-추상 팩토리를 통해 제품군을 생성하기 위한 인터페이스를 제공한다
-다양한 팩토리를 구현할 수 있다.
-클라이언트 코드의 전혀 바뀔 필요 없이, 제품을 여러가지 서로 다른 방식으로 구현 할 수 있다.
-피자주문하기
1. 뉴욕 피자가게(NYPizzaStore)가 필요하다.
PizzaStore nyPizzaStore = new NYPizzaStore();
2. 주문시작
nyPizzaStore.orderPizza("cheese");
3. orderPizza() 메소드에서 우선 createPizza() 메소드를 호출한다.
Pizza pizza = createPizza("cheese");
4. createPizza() 메소드가 호출되면 원재료 공장이 돌아가기 시작한다.
Pizza pizza = new CheesePizza(nyIngredientFactory);
5. prepare() 메소드가 호출되면 팩토리에 원재료 주문이 들어간다.
void prepare() {
dough = factory.createDough();
sauce = factory.createSauce();
cheese = factory.createCheese();
}
6. 피자준비 끝~! orderPizza() 메소드에서 피자를 굽고, 자르고, 포장한다.
패턴 집중 인터뷰('팩토리 메소드 패턴과 추상 팩토리 패턴')
-공통점
-특징
팩토리 메소드 패턴:
추상 팩토리 패턴: