Skip to content

Commit 44990ff

Browse files
committed
feet: add week3/eunseo file
1 parent 647c965 commit 44990ff

File tree

1 file changed

+352
-0
lines changed

1 file changed

+352
-0
lines changed

week3/eunseo/GoF design pattern.md

+352
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
# GoF 디자인 패턴
2+
3+
4+
GoF는 Gang of Four의 줄임말로, `에리히 감마(Erich Gamma)``리처드 헬름(Richard Helm)``랄프 존슨(Ralph Johnson)``존 블리시데스(John Vlissides)` 4인방을 의미한다.
5+
6+
이들은 소프트웨어 설계에 있어 공통된 문제들에 대한 표준적인 해법과 작명법을 제안한 책을 저술하였다.
7+
8+
이들이 소프트웨어 설계의 노하우를 축적하여 재이용하기 좋은 형태로 특정 규약을 묶어서 정리한 것을 `GoF디자인 패턴`이라 한다.
9+
10+
# 구분 기준
11+
12+
목적에 따라 구분하면 `생성`, `구조`, `행동` 세 가지를 기준으로 나눌 수 있다.
13+
14+
`생성`: 객체를 생성하는 데 관련된 패턴으로 객체가 생성되는 과정의 유연성을 높이고 코드의 유지를 쉽게 함
15+
16+
`구조`: 프로그램 구조에 관련된 패턴으로 프로그램 내의 자료구조나 인터페이스 등 프로그램의 구조를 설계하는 데 활용할 수 있는 패턴들
17+
18+
`행동`: 반복적으로 사용되는 객체들의 상호작용을 패턴화 해놓은 것들
19+
20+
# GoF디자인 패턴 종류
21+
22+
| 생성(Creational) | 구조(Structural) | 행동(Behavioral) |
23+
| --- | --- | --- |
24+
| Abstract Factory(추상 팩토리) | Adapter(어댑터) | Chain of Responsibility(책임 연쇄) |
25+
| Builder(빌더) | Bridge(브릿지) | Command(커맨드) |
26+
| Factory Method(팩토리 메서드) | Composite(컴포지트) | Interpreter(인터프리터) |
27+
| Prototype(프로토타입) | Facade(퍼싸드) | Iterator(반복자) |
28+
| Singleton(싱글톤) | Flyweight(플라이웨이트) | Mediator(중재자) |
29+
| | Proxy(프록시) | Memento(메멘토) |
30+
| | | Observer(옵저버) |
31+
| | | State(상태) |
32+
| | | Strategy(전략) |
33+
| | | Template Method(템플릿 메소드) |
34+
| | | Visitor(방문자) |
35+
36+
## 생성 패턴(Creational Pattern)
37+
38+
생성 패턴에는 `Factory Method 패턴`, `Abstract Factory 패턴`이 존재한다.
39+
40+
**이 두가지를 이해하기 위해서는 `Simple Factory`에 대해 알아야 한다.**
41+
42+
### Simple Factory란?
43+
44+
객체를 생성해내는 공장을 따로 두는 것을 의미한다.
45+
46+
즉, `객체 생성 부분을 전담하는 클래스`가 따로 있는 것이다.
47+
48+
Simple Factory를 설명하기 위해 피자로 예를 드는 경우가 많았다.
49+
50+
우리도, 피자를 예를 들어 생각해보자.
51+
52+
피자는 피자 가게에서 직접 생성하지 않고 피자 공장을 거쳐 생성하도록 하는 것이다.
53+
54+
![Untitled](GoF%20%E1%84%83%E1%85%B5%E1%84%8C%E1%85%A1%E1%84%8B%E1%85%B5%E1%86%AB%20%E1%84%91%E1%85%A2%E1%84%90%E1%85%A5%E1%86%AB%20918d1962e3d940b294c33b189145f5c5/Untitled.png)
55+
56+
위 사진은 Star UML로 만든 Pizza 관련 Class Diagram이다.
57+
58+
→와 같은 화살표는 연관이 있다, 정도로 생각하면 된다. (화살표가 향하는 방향에 해당하는 클래스를 사용한다 정도)
59+
60+
화살표가 더 두꺼운 것 (ex CheesePizza→Pizza)의 경우, 화살표가 향하는 쪽이 부모, 화살표가 나오는 쪽이 자식으로, 상속받았다는 것을 의미한다.
61+
62+
상속을 받았다는 것은, 자바의 중요한 성질중 하나인 `다형성`을 활용할 수 있다는 것이다!
63+
64+
위 사진 중에 SimplePizzaFactory가 바로 객체의 생성을 담당하는 클래스로, Simple Factory를 적용한 예시이다.
65+
66+
```java
67+
public class PizzaStore {
68+
SimplePizzaFactory factory;
69+
70+
71+
public PizzaStore (SimplePizzaFactory factory) {
72+
this.factory = factory;
73+
}
74+
75+
public Pizza orderPizza (String type) {
76+
Pizza pizza;
77+
78+
pizza = factory.createPizza(type); // 팩토리를 써서 pizza 객체를 만든다.
79+
80+
** pizza.prepare();
81+
pizza.bake();
82+
pizza.cut();
83+
pizza.box();
84+
85+
return pizza;
86+
}
87+
}
88+
```
89+
90+
```java
91+
public class SimplePizzaFactory
92+
{
93+
public Pizza createPizza(String type)
94+
{
95+
Pizza pizza = null;
96+
97+
if( type.equals("cheese") )
98+
pizza = new CheesePizza();
99+
else if( type.equals("pepperoni") )
100+
pizza = new PepperoniPizza();
101+
else if( type.equals("clam") )
102+
pizza = new ClamPizza();
103+
104+
return pizza;
105+
}
106+
}
107+
```
108+
109+
위 두 코드가 Simple Factory를 사용한 예시이다.
110+
111+
앞서 말한 Factory 클래스가 Pizza 객체의 생성을 담당하고 있는 것을 볼 수 있다.
112+
113+
장점
114+
115+
이를 활용하면 객체를 생성하는 작업을 한 곳에 모아, 객체 생성 부분에 수정 사항이 있을 때 Factory 클래스만 변경해 주면 된다는 장점을 갖고 있다. 즉, 수정 용이, 유지 보수 비용 절감의 효과가 있다.
116+
117+
### 팩토리 메서드 패턴
118+
119+
조건에 따른 객체 생성을 팩토리 클래스로 위임하여, 팩토리 클래스에서 객체를 생성하는 패턴을 의미한다.
120+
121+
위의 Simple Factory를 예로 들면, createPizza가 PizzaFactory에 선언되어있는 것을 확인할 수 있었다.
122+
123+
여기서 createPizza를 추상 메서드로 만든다고 해보자.
124+
125+
![Untitled](GoF%20%E1%84%83%E1%85%B5%E1%84%8C%E1%85%A1%E1%84%8B%E1%85%B5%E1%86%AB%20%E1%84%91%E1%85%A2%E1%84%90%E1%85%A5%E1%86%AB%20918d1962e3d940b294c33b189145f5c5/Untitled%201.png)
126+
127+
그렇다면 위 다이어그램과 같은 형태가 될 것이다.
128+
129+
여기서 Factory가 사용되어 `객체 생성`을 담당하는 클래스가 따로 존재한다는 것을 알고 넘어가자.
130+
131+
구현된 코드를 확인해보면
132+
133+
```java
134+
public abstract class PizzaStore {
135+
public Pizza orderPizza(String type) {
136+
Pizza pizza;
137+
138+
pizza = createPizza(type); // 팩토리 객체가 아닌 Pizza store에 있는 createPizza를 호출함
139+
140+
pizza.prepare();
141+
pizza.bake();
142+
pizza.cut();
143+
pizza.box();
144+
145+
return pizza;
146+
}
147+
148+
abstract Pizza createPizza(String type); // 팩토리 메소드
149+
}
150+
```
151+
152+
```java
153+
// NYPizzaStore
154+
public class ChicagoPizzaStore extends PizzaStore {
155+
Pizza createPizza(String item) {
156+
if( item.equals("cheese") )
157+
return new ChicagoStyleCheesePizza();
158+
else if( item.equals("pepperoni") )
159+
return new ChicagoStylePepperoniPizza();
160+
else if( item.equals("clam") )
161+
return new ChicagoStyleClamPizza();
162+
else
163+
return null;
164+
}
165+
}
166+
```
167+
168+
여기서 주목해서 봐야 할 점은 PizzaStore 클래스에서 createPizza()라는 메서드가 추상 메서드로 되어있다는 점이다.
169+
170+
```java
171+
public static void main(){
172+
PizzaStore pizzaStore = new ChicagoPizzaStore();
173+
174+
pizzaStore.orderPizza("cheese"); // => ChicagoStyleCheesePizza
175+
176+
}
177+
```
178+
179+
이를 사용하는 메인 함수를 생각해보자.
180+
181+
여기서 pizza를 조건에 따라 직접 new 하여 생성하는 것이 아니라, pizzaStore에 따라 다른 Cheese 피자를 생성해준다는 것을 확인할 수 있다.
182+
183+
객체 생성을 직접 하지 않고 서브 클래스에 위임하여 의존성을 제거한다는 점이 중요하다.
184+
185+
여기서 만약, ChicagoPizzaStore이 아닌 NYPizzaStore에서 주문하기를 원하면,
186+
187+
`new ChicagoPizzaStore()``new NYPizzaStore()`으로만 바꾸면 된다.
188+
189+
orderPizza에서의 파라미터인 `cheese`를 변경할 필요가 없어진다.
190+
191+
즉 FactoryMethod Pattern 사용의 장점은
192+
193+
- 클래스 객체를 생성하는 부분, 사용하는 부분 분리하여 낮은 결합도
194+
- 다른 클래스를 추가하거나 클래스 객체의 구성을 변경시키더라도 객체를 생성하는 부분은 건드릴 필요가 없음
195+
196+
### 추상 팩토리 패턴(Abstract Factory Pattern)
197+
198+
구체적인 클래스에 의존하지 않고, 서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공(추상화)하는 패턴
199+
200+
관련성 있는 여러 종류의 객체를 일관된 방식으로 생성하는 경우 사용된다.
201+
202+
![Untitled](GoF%20%E1%84%83%E1%85%B5%E1%84%8C%E1%85%A1%E1%84%8B%E1%85%B5%E1%86%AB%20%E1%84%91%E1%85%A2%E1%84%90%E1%85%A5%E1%86%AB%20918d1962e3d940b294c33b189145f5c5/Untitled%202.png)
203+
204+
이제, 피자에 들어가는 구성요소를 생각해보자.
205+
206+
도우가 있을 것이고, 소스가 있을 것이다.
207+
208+
시카고 피자는 도우가 두껍고, NY피자는 도우가 얇다고 생각해보자.
209+
210+
```java
211+
public interface PizzaIngredientFactory {
212+
public Dough createDough();
213+
public Sauce createSauce();
214+
public Cheese createCheese();
215+
public Pepperoni createPepperoni();
216+
}
217+
```
218+
219+
```java
220+
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
221+
public Dough createDough() {
222+
return new ThinCrustDough();
223+
}
224+
public Sauce createSauce() {
225+
return new MarinaraSauce();
226+
}
227+
public Cheese createCheese() {
228+
return new ReggianoCheese();
229+
}
230+
public Pepperoni createPepperoni() {
231+
return new SlicePepperoni();
232+
}
233+
public Clams createClam() {
234+
return new FreshClams();
235+
}
236+
}
237+
```
238+
239+
위 처럼 재료를 생성하는 부분을 담당하는 Factory를 인터페이스로 만들고, chicago와 NY의 피자 공장이 이를 상속받는다. 여기서, 메서드를 오버라이딩 하며 어떤 재료를 쓸지를 정해준다.
240+
241+
```java
242+
public class CheesePizza extends Pizza {
243+
PizzaIngredientFactory ingredientFactory;
244+
245+
// 각 피자 클래스에서는 생성자를 통해서 팩토리를 전달 받는다.
246+
// 이 팩토리는 인스턴스 변수에 저장한다.
247+
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
248+
this.ingredientFactory = ingredientFactory;
249+
}
250+
251+
void prepare() {
252+
System.out.println("Preparing " + name);
253+
dough = ingredientFactory.createDough();
254+
sauce = ingredientFactory.createSauce();
255+
cheese = ingredientFactory.createCheese();
256+
// 재료가 필요할 때마다 팩토리에 있는 메소드를 호출해서 만들어 온다.
257+
}
258+
}
259+
```
260+
261+
Cheese피자를 생성할 때 NYPizzaStore은 당연하게 NYPizzaIngredientFactor을 사용할 것이다. 이를 인자 값으로 하여 CheesePizza를 생성하면, NYPizzaIngredientFactor에서 NY 피자에 맞는 재료들로 CheesePizza를 만들어 준다.
262+
263+
즉, cheese피자를 생성하고자 할 때, 상위 클래스는 도우가 얇은지 두꺼운지를 신경 쓸 필요가 없이 단순히 cheese피자를 생성한다! 고만 일러주면 하위 클래스가 어떤 도우를 사용할지 정하는 것이다.
264+
265+
```java
266+
public class NYPizzaStore extends PizzaStore {
267+
protected Pizza createPizza(String item) {
268+
Pizza pizza = null;
269+
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
270+
// 뉴욕 피자 가게에는 뉴욕 피자 원재료 공장을 전달해줘야 한다.
271+
// 뉴욕풍 피자를 만들기 위한 재료는 이 공장에서 공급된다.
272+
273+
if( item.equals("cheese") ) {
274+
pizza = new CheesePizza(ingredientFactory);
275+
// 피자 재료를 위해 쓸 팩토리를 각 피자 객체에 전달해준다.
276+
pizza.setName("New York Style Cheese Pizza");
277+
} else if( item.equals("clam") ) {
278+
pizza = new ClamPizza(ingredientFactory);
279+
pizza.setName("New York Style Clam Pizza");
280+
} else if( item.equals("pepperoni") ) {
281+
pizza = new PepperoniPizza(ingredientFactory);
282+
pizza.setName("New York Style Pepperoni Pizza");
283+
}
284+
// 각 형식의 피자마다 새로운 Pizza인스턴스를 만들고 원재료를 공급 받는데 필요한 팩토리를 지정해정해준다.
285+
return pizza;
286+
}
287+
}
288+
```
289+
290+
추상 팩토리의 장점은 구상 클래스에 의존하지 않고 서로 연관된 객체들로 구성할 수 있다는 장점
291+
292+
Pizza 공장에 따라 어떤 재료 객체를 사용할 지 정하게 됨
293+
294+
→ 메인에서는, 어떤 피자 가게가 두꺼운 도우를 쓰는지, 얇은 도우를 쓰는지 신경쓸 필요가 없다!
295+
296+
→ 하위 클래스가 잘 만들어 줌!
297+
298+
```java
299+
public static void main(){
300+
PizzaStore pizzaStore = new NYPizzaStore();
301+
cheesePizza = pizzaStore.createPizza("cheese");
302+
303+
}
304+
```
305+
306+
결국 알아야 할 것은
307+
308+
인터페이스로 캡슐화하여 안의 내용을 보이지 않게 하고, (알 필요가 없게 하고)
309+
310+
그럼으로써 결합도를 낮춘다는 장점이 있다는 것이다.
311+
312+
사실 다형성을 사용한 예로, 객체를 생성하는 부분과 사용하는 부분을 나누었다는 점,
313+
314+
이 때문에 변경 시 용이하다는 점만 기억하면 될 것 같다.
315+
316+
~~팩토리에 대해 공부하는데, 듣는 사람의 머리 속에는 피자밖에 남지 않을 것 같다 ^^~~
317+
318+
~~마치 클래스 공부하는데 머리 속에 붕어빵밖에 남지 않은 것처럼 …~~
319+
320+
여기서 알 수 없는 한가지
321+
322+
다형성으로 해결할 수 있는 문제고
323+
324+
이를 잘 사용하고 있는데,
325+
326+
어떤 상황에서 사용해야 이 패턴이 유용하게 쓰일까?
327+
328+
### **적용 가능성**
329+
330+
다음과 같은 경우 추상 팩토리 패턴을 사용하십시오.
331+
332+
- 시스템은 제품이 생성, 구성 및 표현되는 방식과 독립적이어야 합니다.
333+
- 시스템은 여러 제품군 중 하나로 구성되어야 합니다.
334+
- 관련 제품 개체 제품군은 함께 사용하도록 설계되었으며 이 제약 조건을 적용해야 합니다.
335+
- 제품의 클래스 라이브러리를 제공하고 구현이 아닌 인터페이스만 공개하려는 경우
336+
- 종속성의 수명은 개념적으로 소비자의 수명보다 짧습니다.
337+
- 특정 종속성을 구성하려면 런타임 값이 필요합니다.
338+
- 런타임에 제품군에서 호출할 제품을 결정하려고 합니다.
339+
- 종속성을 해결하기 전에 런타임에만 알려진 하나 이상의 매개변수를 제공해야 합니다.
340+
- 제품 간의 일관성이 필요할 때
341+
- 프로그램에 새 제품이나 제품군을 추가할 때 기존 코드를 변경하고 싶지 않습니다.
342+
343+
라고 나와있다.
344+
345+
번역체지만 앞에서 언급한 내용과 비슷하다.
346+
347+
객체 생성하는 부분과 사용하는 부분을 분리하고, 같은 기능을 하는 클래스끼리 묶어 인터페이스를 생성해야 한다,
348+
349+
어떤 객체를 선택할 지는 런타임 시간에 알 수 있다.
350+
등 .
351+
352+
s

0 commit comments

Comments
 (0)