Pizza OrderPizza()
{
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
피자의 종류가 여러 개이므로 종류를 전달하기 위한 인자를 첨가합니다.
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("pepperoni"))
{
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
type이라는 파라미터를 사용하여 종류에 대한 정보를 전달하였습니다.
종류가 결정되면 if절을 통하여 구상클래스의 인스턴스를 만들고 pizza 필드에 대입합니다(물론 여기 나오는 모든 피자 클래스는 Pizza 인터페이스를 구현한다고 가정합니다.
그런데 이후에 신제품이 출시되었고, 잘 팔리지 않는 피자 종류는 제외시키기로 결정했습니다. 이 경우 코드에서 if 절이 대폭 수정되어야 하고, 나머지는 동일합니다.
바뀌는 부분과 바뀌지 않는 부분이 파악되었으니 캡슐화를 하는 것이 현명합니다.
바뀌는 부분은 피자 종류를 판별하고 피자 객체를 생성하는 부분입니다. 이 부분을 분리하고 SimplePizzaFactory라고 부르기로 합니다(객체 생성을 처리하는 클래스를 팩토리라고 부릅니다).
SimplePizzaFactory 클래스
이제 팩토리 부분을 따로 클래스로 만듭니다.
메뉴의 수정 부분을 반영했습니다.
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();
}
}
}
| cs |
클래스 내부에 CreatePizza()라는 메소드를 정의해 객체 인스턴스 생성을 담당하게 했습니다.
이제 OrderPizza라는 메소드가 포함된 PizzaStore라는 클라이언트 클래스를 위의 팩토리를 이용하여 수정합니다.
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;
}
//기타 메소드
}
| cs |
내부에 SimplePizzaFactory에 대한 레퍼런스를 만들고, 생성자로 객체를 전달하게 만들고, OrderPizza() 메소드를 사용할 때 팩토리 객체를 이용해서 인스턴스를 생성합니다.
Simple Factory
지금 사용한 방법을 Simple Factory라고 부릅니다. Simple Factory는 엄밀히 말해 디자인 패턴이라고 할 수는 없지만 프로그래밍에 있어서 자주 사용되는 방법이므로 알아두는게 좋습니다. 이제부터 더 복잡한 진짜 팩토리 패턴으로 들어갑니다.
피자 프랜차이즈
사업이 번창하여 다른 지역에도 프랜차이즈를 열게 되었습니다. 그런데 지역마다 피자의 스타일이 조금씩 달라서 기존의 코드를 수정해야할 필요가 생겼습니다.
PizzaStore 클래스
각 분점마다의 스타일을 살리기 위해 CreatePizza() 메소드를 다시 살립니다. 대신 추상메소드로 선언하여 PizzaStore를 상속받는 지역 스토어가 각기 구현하도록 할 예정입니다.
public abstract class PizzaStore
{
public Pizza OrderPiazza(string type)
{
Pizza pizza;
pizza = CreatePizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza CreatePizza(string type);
}
{
public Pizza OrderPiazza(string type)
{
Pizza pizza;
pizza = CreatePizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza CreatePizza(string type);
}
이제 각 분점을 위한 서브클래스가 필요합니다. 그리고 서브클래스에서 피자 스타일을 결정하도록 합니다. 주문 과정은 같으므로 이미 PizzaStore 클래스에 마련했습니다. PizzaStore는 어떤 피자가 주어지는지는 전혀 알지 못하고 각 서브클래스에서 넘겨 주는 피자를 받아 처리할 뿐입니다.
이제 뉴욕 스타일 피자 분점 클래스를 만들어 보도록 합니다.
public class NYPizzaStore : PizzaStore
{
protected override 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;
}
}
| cs |
대략 이런 식이 될 것입니다.
피자를 주문하고 만들어지는 과정은 이제 다음과 같습니다.
- 지역스토어 생성(팩토리 역할을 합니다)
- 주문: 여기까지가 외부에서 하는 일입니다
- 내부적으로 OrderPizza()가 CreatePizza() 호출
- 피자를 넘겨받아 작업 마무리
이제 각 분점에서 만드는 피자의 구상 서브클래스를 만들어야 하는데 아직 기본이 되는 Pizza 클래스를 구현하지 않았네요.
Pizza 추상 클래스를 다음과 같이 작성합니다.
public abstract class Pizza
{
public string Name { get; set; }
public string Dough { get; set; }
public string Sauce { get; set; }
public List<string> Toppings = new List<string>();
public void prepare()
{
Console.WriteLine("Preparing " + Name);
Console.WriteLine("Tossing dough...");
Console.WriteLine("Adding sauce...");
Console.WriteLine("Adding toppings...");
foreach(string topping in Toppings)
{
Console.WriteLine(" " + topping);
}
}
public void bake()
{
Console.WriteLine("350도에서 25분간 굽습니다.");
}
public void cut()
{
Console.WriteLine("피자를 부채꼴로 자릅니다.");
}
public void box()
{
Console.WriteLine("공식 피자 상자에 포장합니다.");
}
}
| cs |
이 Pizza 클래스를 기반으로 뉴욕풍 치즈피자 클래스를 만들어 봅니다.
public class NYStyleCheesePizza :Pizza
{
public NYStyleCheesePizza()
{
Name = "NY style 소스와 치즈 피자";
Dough = "Thin Crust Pizza";
Sauce = "Marinara 소스";
Toppings.Add("Grated Reggiano Cheese");
}
}
| cs |
나머지 피자들도 다 구현했다는 가정하에 테스트를 돌려보면...
static void Main(string[] args)
{
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.OrderPizza("cheese");
Console.WriteLine("철수 씨는 {0}를 주문했습니다.", pizza.Name);
}
| cs |
결과)
Preparing NY style 소스와 치즈 피자
Tossing dough...
Adding sauce...
Adding toppings...
Grated Reggiano Cheese
350도에서 25분간 굽습니다.
피자를 부채꼴로 자릅니다.
공식 피자 상자에 포장합니다.
철수 씨는 NY style 소스와 치즈 피자를 주문했습니다.
팩토리 메소드 패턴
지금까지 여기에 등장한 클래스의 종류는 크게 두가지입니다.
하나는 PizzaStore와 그로부터 파생한 분점인 생산자(creator)
두 번째는 Pizza와 그로부터 파생된 각 피자인 제품(product) 클래스입니다.
이 두 종류의 클래스들은 각각 생산자와 제품에 해당하는 정보를 책임지며 서로 병렬 클래스 구조를 이루고 있습니다.
팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하고, 구체적으로 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정합니다.
생산자는 실제 만들어질 제품에 대한 사전 지식이 전혀 없고, 서브 클래스에서 제품을 결정하여 인스턴스를 만드는 것입니다.
지금까지 작성한 코드 전체는 이렇습니다.
using System;
using System.Collections.Generic;
namespace FactoryPattern
{
// 피자 추상 클래스
public abstract class Pizza
{
public string Name { get; set; }
public string Dough { get; set; }
public string Sauce { get; set; }
public List<string> Toppings = new List<string>();
public void prepare()
{
Console.WriteLine("Preparing " + Name);
Console.WriteLine("Tossing dough...");
Console.WriteLine("Adding sauce...");
Console.WriteLine("Adding toppings...");
foreach(string topping in Toppings)
{
Console.WriteLine(" " + topping);
}
}
public void bake()
{
Console.WriteLine("350도에서 25분간 굽습니다.");
}
public void cut()
{
Console.WriteLine("피자를 부채꼴로 자릅니다.");
}
public void box()
{
Console.WriteLine("공식 피자 상자에 포장합니다.");
}
}
// PizzaStore 추상 클래스 선언
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);
}
// 뉴욕 피자 스토어 및 피자 서브 클래스
public class NYPizzaStore : PizzaStore
{
protected override 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 NYStyleCheesePizza :Pizza
{
public NYStyleCheesePizza()
{
Name = "NY style 소스와 치즈 피자";
Dough = "Thin Crust Pizza";
Sauce = "Marinara 소스";
Toppings.Add("Grated Reggiano Cheese");
}
}
public class NYStyleVeggiePizza :Pizza
{
//내용
}
public class NYStyleClamPizza :Pizza
{
//내용
}
public class NYStylePepperoniPizza :Pizza
{
//내용
}
// 시카고 스타일의 스토어 및 피자 서브클래스 구현
// 캘리포니아 스타일의 스토어 및 피자 서브클래스 구현
class Program
{
static void Main(string[] args)
{
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.OrderPizza("cheese");
Console.WriteLine("철수 씨는 {0}를 주문했습니다.", pizza.Name);
}
}
}
| cs |
의존성 뒤집기 원칙
만약 위와 같이 팩토리 패턴을 이용하여 코드를 작성하지 않고, PizzaStore 내부에서 pizza 스타일을 판별하고, 준비하는 코드를 작성했다고 해 봅시다.
그 코드는 대략 다음과 같이 될 것입니다.
public class DependentPizzaStore {
public Pizza CreatePizza ( string style, string style )
{
Pizza pizza = null;
if (style.Equals("NY"))
{
if (type.Equals("cheese")
pizza = new NYStyleCheesePizza();
else if // 이하 스타일 판별 코드
} else if (style.Equals("Chicago"))
{
// 중략
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
이런 식의 코드에서 PizzaStore는 각 피자 객체에 의존하게 됩니다. 팩토리에 맡겨서 만든 것이 아니라 내부에서 직접 만들었기 때문이죠.
디자인 원칙: 추상화된 것에 의존하게 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다.
고수준 구성요소인 추상화에 의존하여야지, 저수준인 구상에 의존하게 해서는 안된다는 뜻입니다. 그렇게 할때 일반적인 생각의 흐름과는 반대로 거슬러 올라가게 되므로, 뒤집기라는 말이 붙었습니다.
이 원칙을 지키려고 할 때 도움이 될 만한 가이드라인은 다음과 같습니다.
- 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 않는다
- 구상 클래스에서 유도된 클래스를 만들지 않는다.
- 베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드 하지 않는다.
이 모든 것이 의존성을 낮추려는 의도라 생각됩니다. 직관적으로 생각해봐도, 예를 들어 "양심을 지키자"라는 대원칙에 의해 행동하게 된다면 마주치게 되는 대부분의 경우에 유연하게 대처할 수 있는 반면, 1-1000까지 써 있는 각종 규칙집에 따라 행동하게 된다면 좀 더 빡빡해지겠죠.
피자 가게에 큰 변화가 생겼습니다. 분점이 많아지다 보니 각 분점마다 좋은 품질의 재료를 공급하는데 차질이 생기는 것입니다. 그래서 원재료 공장을 세워 분점에 배달되도록 하려 합니다.
먼저 각 지방의 메뉴 및 재료를 살펴봅시다(책하고는 좀 다릅니다. 임의로 약간 수정했습니다).
뉴욕:
Dough - thin crust dough
Sauce - Marinara sauce
Cheese -Reggiano cheese
Veggies - Garlic, Onion, Mushroom, Red pepper
Pepperoni - sliced pepperoni
Clam - Fresh clam
시카고:
Dough - thick crust dough
Sauce - plum tomato sauce
Cheese - Mozzarella cheese
Veggies - Black olive, egg plant, spinach
Pepperoni - sliced pepperoni
Clam - Frozen clam
캘리포니아:
Dough - Very thin crust dough
Sauce - Bruschetta sauce
Cheese - Goat cheese
Veggies - onion, mushroom, egg plant
Pepperoni - sliced pepperoni
Clam - Frozen Clam
먼저 각 지방의 메뉴 및 재료를 살펴봅시다(책하고는 좀 다릅니다. 임의로 약간 수정했습니다).
뉴욕:
Dough - thin crust dough
Sauce - Marinara sauce
Cheese -Reggiano cheese
Veggies - Garlic, Onion, Mushroom, Red pepper
Pepperoni - sliced pepperoni
Clam - Fresh clam
시카고:
Dough - thick crust dough
Sauce - plum tomato sauce
Cheese - Mozzarella cheese
Veggies - Black olive, egg plant, spinach
Pepperoni - sliced pepperoni
Clam - Frozen clam
캘리포니아:
Dough - Very thin crust dough
Sauce - Bruschetta sauce
Cheese - Goat cheese
Veggies - onion, mushroom, egg plant
Pepperoni - sliced pepperoni
Clam - Frozen Clam
상당히 많은 부분이 수정되어야 할 것 같습니다. 먼저 공장 인터페이스가 있어야 하겠고, 기본 재료에 대한 인터페이스, 그를 구현하는 개별 재료에 대한 클래스, 그 재료들을 기반으로 한 각 지역별 원재료 공장(지역마다 재료가 다르므로) 코드가 필요합니다.
뉴욕을 중심으로 살펴봅시다.
먼저 원재료 공장의 인터페이스는 어떤 식으로 구현되어야 할까요?
public interface PizzaIngredientFactory
{
Dough CreateDough();
Sauce CreateSauce();
Cheese CreateCheese();
List<Veggies> CreateVeggies();
Pepperoni CreatePepperoni();
Clams CreateClam();
}
| cs |
Dough, Sauce와 같은 클래스가 갑자기 등장했습니다. 자세한 세부 재료 클래스는 다음에 구현하기로 하고 먼저 재료공장 인터페이스가 어떻게 구현되어야 하는지에 대해서만 살핍니다. 다섯 개의 메소드가 안에 선언되어 있습니다.
이것을 상속받아 세 개의 지역별 원재료 공장 코드를 작성합니다. 그 중 뉴욕만 봅시다.
public class NYPizzaIngredientFactory : PizzaIngredientFactory
{
public Dough CreateDough()
{
return new ThinCrustDough();
}
public Sauce CreateSauce()
{
return new MarinaraSauce();
}
public Cheese CreateCheese()
{
return new ReggianoCheese();
}
public List<Veggies> CreateVeggies()
{
var veggies = new List<Veggies>{ new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
return veggies;
}
public Pepperoni CreatePepperoni()
{
return new SlicedPepperoni();
}
public Clams CreateClam()
{
return new FreshClams();
}
}
| cs |
메소드들을 구현했습니다. 여기서 세부 재료가 결정됩니다.
이제 재료 클래스를 만들어 볼까요?
먼저 각 재료의 인터페이스를 만듭니다.
public interface Dough
{
string ToString();
}
public interface Sauce
{
string ToString();
}
public interface Cheese
{
string ToString();
}
public interface Veggies
{
string ToString();
}
public interface Pepperoni
{
string ToString();
}
public interface Clams
{
string ToString();
}
| cs |
이름을 출력하기 위한 메소드만 들어있습니다.
이것을 상속받아 각 재료 구상 클래스를 만듭니다.
// 재료 1. Dough
public class ThinCrustDough : Dough
{
public override string ToString()
{
return "Thin Crust Dough";
}
}
public class ThickCrustDough : Dough
{
//
}
public class VeryThinCrustDough : Dough
{
//
}
// 재료 2. Sauce
public class MarinaraSauce : Sauce
{
public override string ToString()
{
return "Marinara Sauce";
}
}
public class PlumTomatoSauce : Sauce
{
//
}
public class BruschettaSauce : Sauce
{
//
}
// 재료 3. Cheese
// 재료 4. Veggies
// 재료 5. Pepperoni
// 재료 6. Clams
| cs |
전부 작성한 것은 아니지만 대략의 얼개를 보실 수 있을 것입니다. 어차피 내부 코드라야 이름에 해당하는 것을 리턴해주는 것 뿐이니까요.
재료가 완성되고, 원재료 공장이 완성 되었으니 팩토리 메소드가 있는 Pizza 클래스도 수정해야 합니다. 추상 클래스 먼저 봅시다.
public abstract class Pizza
{
public string Name { get; set; }
public Dough Dough { get; set; }
public Sauce Sauce { get; set; }
public List<Veggies> veggies { get; set; }
public Cheese cheese { get; set; }
public Pepperoni pepperoni { get; set; }
public Clams clam { get; set; }
public abstract void prepare();
public void bake()
{
Console.WriteLine("350도에서 25분간 굽습니다.");
}
public void cut()
{
Console.WriteLine("피자를 부채꼴로 자릅니다.");
}
public void box()
{
Console.WriteLine("공식 피자 상자에 포장합니다.");
}
public override string ToString()
{
//피자 이름 출력 코드
var 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 (int i = 0; i < veggies.Count; i++)
{
result.Append(veggies[i]);
if (i < veggies.Count - 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();
}
}
| cs |
재료마다 클래스가 있는 것으로 바꾸었으므로 클래스로 수정해 주고, prepare()는 추상으로 바꾸었습니다. 구상 클래스에서 prepare() 메소드로 필요한 재료를 정돈합니다. 그 외의 다른 코드는 바뀌지 않고, 구체적인 이름을 출력하는 메소드인 ToString()을 추가하였습니다.
다음은 구상 Pizza 클래스를 작성해야 합니다.
이제 뉴욕, 시카고, 캘리포니아 중 어떤 스타일의 피자인지는 원재료 공장 수준에서 결정하게 되므로 Pizza는 CheesePizza, ClamPizza, VeggiePizza, PepperoniPizza로만 작성합니다.
public class CheesePizza : Pizza
{
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory)
{
this.ingredientFactory = ingredientFactory;
}
public override void prepare()
{
Console.WriteLine("preparing " + Name);
Dough = ingredientFactory.CreateDough();
Sauce = ingredientFactory.CreateSauce();
cheese = ingredientFactory.CreateCheese();
}
}
public class ClamPizza : Pizza
{
//
}
public class PepperoniPizza : Pizza
{
//
}
public class VeggiePizza : Pizza
{
//
}
| cs |
내부에 원재료 공장의 레퍼런스를 두고 생성자를 통해 어느 공장인지를 제공받습니다.
그리고 그 재료에 따라 prepare 메소드로 재료를 준비합니다. 피자 클래스는 어느 지역인지를 신경쓰지 않습니다. 다만 재료를 전달받고 준비할 뿐입니다.
다음 단계는 PizzaStore 차례입니다.
NYPizzaStore는 다음처럽 만듭니다.
public class NYPizzaStore : PizzaStore
{
protected override Pizza CreatePizza(string item)
{
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (item.Equals("cheese"))
{
pizza = new CheesePizza(ingredientFactory);
pizza.Name = "New York Style Cheese Pizza";
}
else if (item.Equals("veggie"))
{
pizza = new VeggiePizza(ingredientFactory);
pizza.Name = "New York Style Veggie Pizza";
}
else if (item.Equals("clam"))
{
pizza = new ClamPizza(ingredientFactory);
pizza.Name = "New York Style Clam Pizza";
}
else if (item.Equals("pepperoni"))
{
pizza = new PepperoniPizza(ingredientFactory);
pizza.Name = "New York Style Pepperoni Pizza";
}
return pizza;
}
}
| cs |
생성자 내에 뉴욕 원재료 공장에 대한 레퍼런스가 있습니다. 그리고 인자로 전달받은 종류에 따라 치즈, 야채, 페퍼로니, 조개 피자를 결정합니다.
상당히 복잡한 과정을 거쳤습니다.
추상 팩토리라 하는 새로운 형식의 팩토리를 도입하여 원재료군을 생산하기 위한 방법을 만들었습니다. 이런 방식으로 제품군을 생성하기 위한 인터페이스를 제공하고, 제품을 생산하는 팩토리와는 분리 시켰습니다.
이 코드에서 추상 팩토리 패턴에 해당하는 것은 PizzaIngredientFactory입니다. 그 인터페이스에서 제품군을 생산하기 위한 추상 인터페이스를 제공하고,
NYPizzaIngredientFactory 같은 구상 팩토리 클래스 내부에서 CreateDough()같은 메소드는 팩토리 메소드 패턴으로 구현되었습니다.
두 가지 비슷한 이름의 패턴이 동시에 나와 상당히 혼란스러웠습니다. 결국 따로 뗴어 놓기는 어려운 개념이지만 말이죠. 마지막으로 두 개념을 정리해보고 코드 전체를 첨부하며 마칠까 합니다.
추상 팩토리 패턴: 서로 연관된, 또는 의존적인 객체들로 이루어진 제품군을 생성하기 위한 인터페이스를 제공. 객체 생성은 팩토리 인터페이스에서 선언한 메소드에 의해 이루어집니다. 구상 클래스에 의존하지 않고 서로 다른 객체들로 이루어진 제품군을 만들기 위한 용도로 쓰입니다.
팩토리 메소드 페턴: 객체를 생성하기 위한 인터페이스를 만들며, 어떤 클래스의 인스턴스를 만들 것인가는 서브 클래스에서 결정하도록 합니다. 추상 팩토리 패턴에서 객체 구성을 통해 패턴을 만들었다면, 팩토리 메소드 페턴에서는 상속을 활용하여 객체 생성을 합니다.
이 두 패턴 모두 구상 클래스가 아닌 추상클래스 혹은 인터페이스에 맞춰 코딩을 가능하게 해주는 기법입니다.
using System;
using System.Text;
using System.Collections.Generic;
namespace FactoryPattern
{
// 재료공장 인터페이스 선언
public interface PizzaIngredientFactory
{
Dough CreateDough();
Sauce CreateSauce();
Cheese CreateCheese();
List<Veggies> CreateVeggies();
Pepperoni CreatePepperoni();
Clams CreateClam();
}
// 각 재료 인터페이스
public interface Dough
{
string ToString();
}
public interface Sauce
{
string ToString();
}
public interface Cheese
{
string ToString();
}
public interface Veggies
{
string ToString();
}
public interface Pepperoni
{
string ToString();
}
public interface Clams
{
string ToString();
}
// 구상 재료 클래스들
// 재료 1. Dough
public class ThinCrustDough : Dough
{
public override string ToString()
{
return "Thin Crust Dough";
}
}
public class ThickCrustDough : Dough
{
public override string ToString()
{
return "ThickCrust style extra thick crust dough";
}
}
public class VeryThinCrustDough : Dough
{
public override string ToString()
{
return "Very very thin crust dough";
}
}
// 재료 2. Sauce
public class MarinaraSauce : Sauce
{
public override string ToString()
{
return "Marinara Sauce";
}
}
public class PlumTomatoSauce : Sauce
{
public override string ToString()
{
return "Tomato sauce with plum tomatoes";
}
}
public class BruschettaSauce : Sauce
{
public override string ToString()
{
return "Bruschetta sauce";
}
}
// 재료 3. Cheese
public class ReggianoCheese : Cheese
{
public override string ToString()
{
return "Reggiano Cheese";
}
}
/*public class ParmesanCheese : Cheese
{
public override string ToString()
{
return "Shredded Parmesan";
}
}*/
public class MozzarellaCheese : Cheese
{
public override string ToString()
{
return "Mozzarella Cheese";
}
}
public class GoatCheese : Cheese
{
public override string ToString()
{
return "Goat Cheese";
}
}
// 재료 4. Veggies
public class Garlic : Veggies
{
public override string ToString()
{
return "Garlics";
}
}
public class BlackOlives : Veggies
{
public override string ToString()
{
return "Black Olives";
}
}
public class Onion : Veggies
{
public override string ToString()
{
return "Onions";
}
}
public class Mushroom : Veggies
{
public override string ToString()
{
return "Mushrooms";
}
}
public class RedPepper : Veggies
{
public override string ToString()
{
return "Red Pepper";
}
}
public class EggPlant : Veggies
{
public override string ToString()
{
return "Egg Plant";
}
}
public class Spinach : Veggies
{
public override string ToString()
{
return "Spinach";
}
}
// 재료 5. Pepperoni
public class SlicedPepperoni : Pepperoni
{
public override string ToString()
{
return "Sliced pepperoni";
}
}
// 재료 6. Clams
public class FreshClams : Clams
{
public override string ToString()
{
return "Fresh Clams from Long Island Sound";
}
}
public class FrozenClams : Clams
{
public override string ToString()
{
return "Frozen Clams from Chesapeake Bay";
}
}
// 원재료 공장
public class NYPizzaIngredientFactory : PizzaIngredientFactory
{
public Dough CreateDough()
{
return new ThinCrustDough();
}
public Sauce CreateSauce()
{
return new MarinaraSauce();
}
public Cheese CreateCheese()
{
return new ReggianoCheese();
}
public List<Veggies> CreateVeggies()
{
var veggies = new List<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 ChicagoIngredientFactory : PizzaIngredientFactory
{
public Dough CreateDough()
{
return new ThickCrustDough();
}
public Sauce CreateSauce()
{
return new PlumTomatoSauce();
}
public Cheese CreateCheese()
{
return new MozzarellaCheese();
}
public List<Veggies> CreateVeggies()
{
var veggies = new List<Veggies> { new BlackOlives(), new Spinach(), new EggPlant() };
return veggies;
}
public Pepperoni CreatePepperoni()
{
return new SlicedPepperoni();
}
public Clams CreateClam()
{
return new FrozenClams();
}
}
public class CaliforniaIngredientFactory : PizzaIngredientFactory
{
public Dough CreateDough()
{
return new VeryThinCrustDough();
}
public Sauce CreateSauce()
{
return new BruschettaSauce();
}
public Cheese CreateCheese()
{
return new GoatCheese();
}
public List<Veggies> CreateVeggies()
{
var veggies = new List<Veggies> { new Onion(), new Mushroom(), new EggPlant() };
return veggies;
}
public Pepperoni CreatePepperoni()
{
return new SlicedPepperoni();
}
public Clams CreateClam()
{
return new FrozenClams();
}
}
// 피자 추상 클래스
public abstract class Pizza
{
public string Name { get; set; }
public Dough Dough { get; set; }
public Sauce Sauce { get; set; }
public List<Veggies> veggies { get; set; }
public Cheese cheese { get; set; }
public Pepperoni pepperoni { get; set; }
public Clams clam { get; set; }
public abstract void prepare();
public void bake()
{
Console.WriteLine("350도에서 25분간 굽습니다.");
}
public void cut()
{
Console.WriteLine("피자를 부채꼴로 자릅니다.");
}
public void box()
{
Console.WriteLine("공식 피자 상자에 포장합니다.");
}
public override string ToString()
{
//피자 이름 출력 코드
var 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 (int i = 0; i < veggies.Count; i++)
{
result.Append(veggies[i]);
if (i < veggies.Count - 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();
}
}
// 구상 Pizza 클래스
public class CheesePizza : Pizza
{
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory)
{
this.ingredientFactory = ingredientFactory;
}
public override void prepare()
{
Console.WriteLine("preparing " + Name);
Dough = ingredientFactory.CreateDough();
Sauce = ingredientFactory.CreateSauce();
cheese = ingredientFactory.CreateCheese();
}
}
public class ClamPizza : Pizza
{
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory)
{
this.ingredientFactory = ingredientFactory;
}
public override void prepare()
{
Console.WriteLine("preparing " + Name);
Dough = ingredientFactory.CreateDough();
Sauce = ingredientFactory.CreateSauce();
cheese = ingredientFactory.CreateCheese();
clam = ingredientFactory.CreateClam();
}
}
public class PepperoniPizza : Pizza
{
PizzaIngredientFactory ingredientFactory;
public PepperoniPizza(PizzaIngredientFactory ingredientFactory)
{
this.ingredientFactory = ingredientFactory;
}
public override void prepare()
{
Console.WriteLine("preparing " + Name);
Dough = ingredientFactory.CreateDough();
Sauce = ingredientFactory.CreateSauce();
cheese = ingredientFactory.CreateCheese();
veggies = ingredientFactory.CreateVeggies();
pepperoni = ingredientFactory.CreatePepperoni();
}
}
public class VeggiePizza : Pizza
{
PizzaIngredientFactory ingredientFactory;
public VeggiePizza(PizzaIngredientFactory ingredientFactory)
{
this.ingredientFactory = ingredientFactory;
}
public override void prepare()
{
Console.WriteLine("preparing " + Name);
Dough = ingredientFactory.CreateDough();
Sauce = ingredientFactory.CreateSauce();
cheese = ingredientFactory.CreateCheese();
veggies = ingredientFactory.CreateVeggies();
}
}
// PizzaStore 추상 클래스 선언
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);
}
// 뉴욕 피자 스토어 및 피자 서브 클래스
public class NYPizzaStore : PizzaStore
{
protected override Pizza CreatePizza(string item)
{
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (item.Equals("cheese"))
{
pizza = new CheesePizza(ingredientFactory);
pizza.Name = "New York Style Cheese Pizza";
}
else if (item.Equals("veggie"))
{
pizza = new VeggiePizza(ingredientFactory);
pizza.Name = "New York Style Veggie Pizza";
}
else if (item.Equals("clam"))
{
pizza = new ClamPizza(ingredientFactory);
pizza.Name = "New York Style Clam Pizza";
}
else if (item.Equals("pepperoni"))
{
pizza = new PepperoniPizza(ingredientFactory);
pizza.Name = "New York Style Pepperoni Pizza";
}
return pizza;
}
}
public class ChicagoPizzaStore : PizzaStore
{
protected override Pizza CreatePizza(string item)
{
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new ChicagoIngredientFactory();
if (item.Equals("cheese"))
{
pizza = new CheesePizza(ingredientFactory);
pizza.Name = "Chicago Style Cheese Pizza";
}
else if (item.Equals("veggie"))
{
pizza = new VeggiePizza(ingredientFactory);
pizza.Name = "Chicago Style Veggie Pizza";
}
else if (item.Equals("clam"))
{
pizza = new ClamPizza(ingredientFactory);
pizza.Name = "Chicago Style Clam Pizza";
}
else if (item.Equals("pepperoni"))
{
pizza = new PepperoniPizza(ingredientFactory);
pizza.Name = "Chicago Style Pepperoni Pizza";
}
return pizza;
}
}
public class CaliforniaPizzaStore : PizzaStore
{
protected override Pizza CreatePizza(string item)
{
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new CaliforniaIngredientFactory();
if (item.Equals("cheese"))
{
pizza = new CheesePizza(ingredientFactory);
pizza.Name = "California Style Cheese Pizza";
}
else if (item.Equals("veggie"))
{
pizza = new VeggiePizza(ingredientFactory);
pizza.Name = "California Style Veggie Pizza";
}
else if (item.Equals("clam"))
{
pizza = new ClamPizza(ingredientFactory);
pizza.Name = "California Style Clam Pizza";
}
else if (item.Equals("pepperoni"))
{
pizza = new PepperoniPizza(ingredientFactory);
pizza.Name = "California Style Pepperoni Pizza";
}
return pizza;
}
}
class Program
{
static void Main(string[] args)
{
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza1 = nyStore.OrderPizza("cheese");
Console.WriteLine(pizza1.ToString());
PizzaStore nyStore2 = new NYPizzaStore();
Pizza pizza2 = nyStore2.OrderPizza("veggie");
Console.WriteLine(pizza2.ToString());
PizzaStore chStore = new ChicagoPizzaStore();
Pizza pizza3 = chStore.OrderPizza("clam");
Console.WriteLine(pizza3.ToString());
PizzaStore clStore = new CaliforniaPizzaStore();
Pizza pizza4 = clStore.OrderPizza("pepperoni");
Console.WriteLine(pizza4.ToString());
}
}
}
| cs |
preparing New York Style Cheese Pizza
350도에서 25분간 굽습니다.
피자를 부채꼴로 자릅니다.
공식 피자 상자에 포장합니다.
---- New York Style Cheese Pizza ----
Thin Crust Dough
Marinara Sauce
Reggiano Cheese
preparing New York Style Veggie Pizza
350도에서 25분간 굽습니다.
피자를 부채꼴로 자릅니다.
공식 피자 상자에 포장합니다.
---- New York Style Veggie Pizza ----
Thin Crust Dough
Marinara Sauce
Reggiano Cheese
Garlics, Onions, Mushrooms, Red Pepper
preparing Chicago Style Clam Pizza
350도에서 25분간 굽습니다.
피자를 부채꼴로 자릅니다.
공식 피자 상자에 포장합니다.
---- Chicago Style Clam Pizza ----
ThickCrust style extra thick crust dough
Tomato sauce with plum tomatoes
Mozzarella Cheese
Frozen Clams from Chesapeake Bay
preparing California Style Pepperoni Pizza
350도에서 25분간 굽습니다.
피자를 부채꼴로 자릅니다.
공식 피자 상자에 포장합니다.
---- California Style Pepperoni Pizza ----
Very very thin crust dough
Bruschetta sauce
Goat Cheese
Onions, Mushrooms, Egg Plant
Sliced pepperoni
댓글 없음:
댓글 쓰기