팩토리 패턴(Factory Pattern)

디자인패턴 2014. 11. 11. 14:22

1. 팩토리 패턴이란?


 팩토리는 구상(Concrete) 클래스가 아닌 추상(Abstract) 클래스/인터페이스에 맞춰서 코딩할 수 있게 해 주는 강력한 기법이다. 모든 팩토리 패턴에서는 애플리케이션의 구상(Concrete) 클래스에 대한 의존성을 줄여줌으로써 느슨한 결합(Loose Coupling)을 도와준다. 그리고 객체 생성을 캡슐화(Encapsulation)할 수 있다.

(1) 팩토리 메소드 패턴
 팩토리 메소드 패턴은 클라이언트 코드와 Instance를 만들어야 할 구상(Concrete) 클래스를 분리시켜야 할 때 사용한다. 또한 어떤 구상(Concrete) 클래스를 필요로 하게 될지 미리 알 수 없는 경우에도 매우 유용하다.
 팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 만들고, 어떤 클래스의 Instance를 만들지는 Subclass에게 위임(Delegate)한다. Subclass에서는 팩토리 메소드를 구현하여 객체를 생성한다.

(2) 추상(Abstract) 팩토리 패턴
 추상(Abstract) 팩토리 패턴은 클라이언트에서 구상(Concrete) 클래스에 직접 의존하지 않고도 서로 연관된 일련의 제품들(제품군)을 만들어야 할 때 사용한다.
 추상(Abstract) 팩토리 패턴에서는 객체 구성(Composition)을 활용한다. 객체 생성이 팩토리 인터페이스에서 선언한 메소드들에서 구현된다.



2. 팩토리 패턴 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// 추상(Abstract) 팩토리
// 추상(Abstract) 팩토리를 통해서 제품군(원재료들)을 생성하기 위한 인터페이스를 제공할 수 있다.
// 각 Subclass에서는 지역별 사정에 맞는 재료들을 구현한다.
public interface PizzaIngredientFactory {
 // 추상(Abstract) 팩토리에서 제품을 생산하기 위한 메소드를 팩토리 메소드 패턴으로 구현하는 경우가 있다.
 public Dough createDough();
 public Sauce createSauce();
 public Cheese createCheese();
 public Veggies[] createVeggies();
 public Pepperoni createPepperoni();
 public Clams createClam();
}
 
 
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
 
 @Override
 public Dough createDough() {
  return new ThinCrustDough();
 }
 
 @Override
 public Sauce createSauce() {
  return new MarinaraSauce();
 }
 
 @Override
 public Cheese createCheese() {
  return new ReggianoCheese();
 }
 
 @Override
 public Veggies[] createVeggies() {
  Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
  return veggies;
 }
 
 @Override
 public Pepperoni createPepperoni() {
  return new SlicedPepperoni();
 }
 
 @Override
 public Clams createClam() {
  return new FreshClams();
 }
}
 
 
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
 
 @Override
 public Dough createDough() {
  return new ThickCrustDough();
 }
 
 @Override
 public Sauce createSauce() {
  return new PlumTomatoSauce();
 }
 
 @Override
 public Cheese createCheese() {
  return new Mozzarella();
 }
 
 @Override
 public Veggies[] createVeggies() {
  Veggies veggies[] = { new EggPlant(), new Spinach(), new BlackOlives() };
  return veggies;
 }
 
 @Override
 public Pepperoni createPepperoni() {
  return new SlicedPepperoni();
 }
 
 @Override
 public Clams createClam() {
  return new FrozenClam();
 }
 
}
 
 
import java.util.*;
 
public abstract class Pizza {
 String name;
 String dough;
 String sauce;
 Veggies veggies[];
 Cheese cheese;
 Pepperoni pepperoni;
 Clams clam;
  
 // 피자를 만드는 데 필요한 재료들을 정돈
 // 피자에는 여러 종류가 있고, 지역마다 사용하는 재료가 다르므로 팩토리 메소드를 사용함.
 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 oficial PizzaStore box");
 }
  
 public String getName() {
  return name;
 }
  
 public void setName(String name) {
  this.name = name;
 }
  
}
 
 
public class CheesePizza extends Pizza {
 PizzaIngredientFactory ingredientFactory;
  
 public CheesePizza(PizzaIngredientFactory ingredientFactory) {
  this.ingredientFactory = ingredientFactory;
 }
  
 @Override
 void prepare() {
  System.out.println("Preparing " + name);
  // 사용하는 재료에 따라 피자 종류가 달라진다. 그래서 IngredientFactory에 따라 피자가 다양하게 만들어질 수 있다.
  dough = ingredientFactory.createDough();
  cheese = ingredientFactory.createCheese();
  sauce = ingredientFactory.createSauce();
 }
 
}
 
 
public class ClamPizza extends Pizza {
 PizzaIngredientFactory ingredientFactory;
  
 public ClamPizza(PizzaIngredientFactory ingredientFactory) {
  this.ingredientFactory = ingredientFactory;
 }
  
 @Override
 void prepare() {
  System.out.println("Preparing " + name);
  dough = ingredientFactory.createDough();
  sauce = ingredientFactory.createSauce();
  cheese = ingredientFactory.createCheese();
  clam = ingredientFactory.createClam();
 }
 
}
 
 
public abstract class PizzaStore {
 
 public Pizza orderPizza(String type){
  Pizza pizza;
   
  // createPizza 메소드를 호출하면 PizzaStore를 구현한 Subclass에서 처리가 된다.
  // Pizza 객체의 Instance를 만드는 일은 PizzaStore의 Subclass에 있는 createPizza() 메소드에서 처리한다.
  // 실제로 어떤 구상(Concrete) 클래스에서 작업을 처리되고 있는지 알 수 없기 때문에, PizzaStore와 Pizza 클래스는 서로 분리되어 있다고 할 수 있다.
  pizza = createPizza(type);
   
  pizza.prepare();
  pizza.bake();
  pizza.cut();
  pizza.box();
   
  return pizza;
 }
  
 
 // 팩토리 메소드는 클라이언트(Superclass에 있는 orderPizza()와 같은 코드)에서 실제로 생성되는 Concrete 객체(Pizza 객체)가 무엇인지 알 수 없게 만드는 역할도 한다.
 protected abstract Pizza createPizza(String type);
}
 
 
public class NYPizzaStore extends PizzaStore {
 
 @Override
 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 {
 
 @Override
 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;
 }
}
 
 
public class PizzaTestDrive {
 
 /**
  * @param args
  */
 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");
 }
 
}







참고
Head FIrst Design Pattern : 스토리가 있는 패턴 학습법
http://www.gurubee.net/pages/viewpage.action?pageId=1507401


출처 - http://digitanomad.blogspot.kr/2013/02/1.html

: