Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions nari/04.팩토리패턴.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# 필요성

- 객체의 인스턴스를 만드는 작업이 항상 공개되어야 하는 것은 아니며, 오히려 모든 것을 공개하면 결합 문제가 생길 수 있음
- 구상 클래스를 만들고 new 로 인스턴스 생성할 때 다시 변경이 가능하게 해야 한다.

# 최첨단 피자 코드 만들기

- 피자 생성 (new Pizza)
- 피자 종류를 고를 수 있도록 orderPizza에 type을 받도록 파라미터 지정
- 피자 종류에 따라 구상 클래스 인스턴스 생성
- 피자 만들기 진행

# 피자 코드 추가하기

- 조개 피자와 야채피자 추가, 그리스 스타일 피자 제외
- orderPizza에서 문제가 되는 부분이 인스턴스를 만드는 구상 클래스를 선택하는 부분
=> 여기를 상황이 변하면 코드를 변경해야 함

# 객체 생성 부분 캡슐화하기

- 객체 생성 부분을 orderPizza 메소드에서 뽑아내는 법?

## 새로 만들 객체를 팩토리라고 부르겠습니다

- 객체 생성을 처리하는 클래스를 팩토리(Factory)라고 부름
- SimplePizza Factory를 만들면 orderPizza()메소드는 새로 만든 객체의 클라이언트가 됨
=> 즉, orderPizza가 SimplePizza를 호출

# 객체 생성 팩토리 만들기

- 피자 객체 생성 부분 전담할 클래스 (팩토리) 만들기

```java

public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;

if(type.equals("cheese")){
pizza = new CheesePizza();
}else if (type.equals("peperroni")){
pizza = new PepperoniPizza();
}
...
return pizza;
}
}
```

- 이 때 장점: 변경 시 여기저기 고칠 필요 없이 팩토리 클래스 하나만 고치면 됨
- 정적 팩토리도 있다

# 클라이언트 코드 수정하기

- 팩토리로 피자 객체 생성하도록 고치기

```java
public class PizzaStore {
SimplePizzaFactory factory;

public PizzaStore(SimplePizzaFactor factor) {
this.factory = factory;
}

public Pizza orderPizza(String type) {
Pizza pizza;

pizza = factory.createPizza(type);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

return pizza
}
// 기타 메소드
}
```

# '간단한 팩토리'의 정의

- 디자인패턴 이라기 보다는 프로그래밍에서 자주 쓰이는 관용구

# 다양한 팩토리 만들기

- 다른 지점에도 피자집 내기
- 각 지점마다 그 지역의 특성과 입맛을 반영한 다양한 스타일의 피자 만들기
- PizzaStroe -> NYPizzaFactory, ChicagoPizzaFactory...
- 앞에서 배운 방법: SimplePizzaFactory를 삭제하고 서로 다른 3가지 팩토리(NYPizzaFactory, ChicagoPizzaFactory, CaliforniaPizzaFactory)를 만들고, PizzaStore에서 적당한 팩토리를 사용하도록 하는 방법

## 하지만 지점들을 조금 더 제대로 관리해야 합니다

- 굽는 방식이나 피자를 자르는 것을 까먹는 일 발생
- PizzaStore와 피자 제적 코드 전체를 하나로 묶어주는 프레임워크가 필요 + 유연성
- 피자 가게와 피자 만드는 과정을 하나로 묶는 법?

# 피자 가게 프레임워크 만들기

- createPizza 메소드를 PizzaStore에 다시 넣기
- 대신 추상 메소드로 선언하고, 지역별 스타일에 맞게 PizzaStore의 서브 클래스 만들기
- 그리고 각 지점에 맞는 NYPizzaStore, ChicagoPizzaStore, CaliforniaPizzaStore 등 만들기

# 서브클래스가 결정하는 것 알아보기

- 각 지점마다 달라질 수 있는 것 : 피자 스타일 뿐
- 달라지는 점은 createPizza()메소드에 넣고 그 메소드에서 해당 스타일의 피자를 만들도록 하기
- PizzaStore와 Pizza는 서로 완전히 분리되어 있음
- orderPizza에서 createPizza를 호출하면 Pizza의 서브클래스가 그 호출을 받아 피자를 만듦 -> 가게에 따라 다름
- 피자의 종류는 어떤 서브클래스를 선택했느냐에 따라 결정됨

# 팩토리 메소드 선언하기

- 구상 클래스 인스턴스 만드는 일을 하나의 객체가 전부 처리하는 방식에서 서브 클래스가 처리하는 방식으로 바뀌었음

# 팩토리 메소드 패턴 살펴보기

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

# 팩토리 메소드 패턴의 정의

- 팩토리 메소드 패턴에서는 객체를 생성할 때 필요한 인터페이스를 만듦.
- 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정.
- 팩토리 메소드 패턴을 사용하면 클래스 인스턴스 만드는 일을 서브클래스에게 맡김
=> 사용하는 서브클래스에 따라 생산되는 객체 인스턴스가 결정

# 객체 의존성 살펴보기

- 객체 인스턴스를 직접 만들면 구상 클래스에 의존

# 의존성 뒤집기 원칙

- 추상화된 것에 의존하게 만들고 구상 클래스에 의존하지 않게 만든다
- 항상 추상화에 의존하게 만들어야 함!
117 changes: 117 additions & 0 deletions nari/3.데코레이터패턴.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# 데코레이터 패턴 살펴보기

특정 음료에서 시작해서 첨가물로 그 음료를 장식하기

1. Dark Roast 객체를 가져온다
2. mocha 객체로 장식
3. Whip 객체로 장식
4. cost 메서드 호출

=> 객체를 "장식" 하는 방법?

# 주문 시스템에 데코레이터 패턴 적용하기

1. Dark Roast 객체는 beverage에서 상속 받아서 cost가 있음
2. Mocha 객체를 만들고 그 객체로 DarkRoast 감싸기
3. Whip 데코레이터 만들어 mocha 감싸기
4. whip의 cost 호출하기

# 데코레이터 패턴의 정의

- 데코레이터 패턴으로 객체에 추가 요소를 동적으로 더할 수 있음
- 데코레이터를 사용하면 서브클래스로 만들 때보다 훨씬 유연하게 확장 가능
- 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 일 말고도 추가 작업 수행 가능

# Beverage 클래스 장식하기

- Beverage 라는 추상 클래스
- HouseBlend, DarkRoast, Espresso, Decaf 와 같은 커피 종류마다 구성 요소를 나타내는 구상 클래스
- CondimentDecorator라는 데코레이터 클
- Milk, Mocha, Soy 등등 하위 데코레이터 클래스

- 이때 CondimentDecorator는 Beverage를 상속받는데, 이 상속은 서브클래스가 아니라 형식을 맞추려고 사용하는 방식
- 그래서 데코레이터를 언제든지 구현해서 새로운 행동을 추가할 수 있음
- 형식만 상속하려면 인터페이스로 만들어도 되는데 클래스로 만든 이유
- 원래 데코레이터 패턴에서는 특정한 추상 구성 요소를 지정할 필요가 없음. 그래서 인터페이스를 쓰면 되는데 여기서는 기존 코드가 클래스였기 때문

# 데코레이터 패턴 적용 연습

```java
public abstract class Beverage {
String description = "제목없음";

public String getDescription() {
return description;
}

public abstract double cost();
}
```

- cost는 하위에서 구현 필요

```java
public abstract class CondimentDecorator extends Beverage {
Beverage beverage;
public abstract String getDescription();

}
```

- 어떤 음료든 감쌀 수 있도록 Beverage 슈퍼클래스 유형을 사용
- 모든 첨가물 데코레이터에 getDescription() 메소드를 새로 구현하도록 만들 게획

# 음료 코드 구현하기

```java
public class Espresso extends Beverage {
public HouseBlend() {
description = '하우스 블렌드 커피';
}

public double cost() {
return .89;
}
}
```

# 첨가물 코드 구현하기

```java
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
// 감싸고자 하는 음료를 저장하는 인스턴스 변수
// 인스턴스 변수를 감싸고자 하는 객체로 생성하는 생성자
this.beverage = beverage;
}

public String getDescription(){
// 설명에 음료이름 + 첨가물로 변경
return beverage.getDescription + ", 모카";
}

public double cost(){
// 음료 가격에 모카를 추가한 가격을 계산
return beverage.cost() + .20;
}
}

```

# 커피 주문 시스템 코드 테스트

- 주의점 : 구상 요소로 어떤 작업을 처리하는 코드에 데코레이터 패턴을 적용하면 코드가 제대로 동작하지 않음
- 데코레이터 패턴을 쓰면 관리할 객체가 늘어나니까 실수할 가능성이 높아지지만 실제로는 팩토리나 빌더같은 패턴으로 데코레이터를 만들어서 사용하여 걱정하지 않아도 됨

# 데코레이터가 적용된 예: 자바I/O

- 자바 I/O API
- 파일에서 데이터를 읽어오는 스트림에 기능을 더하는 데코레이터를 사용하는 객체 구현

# java.io 클래스와 데코레이터 패턴

- InputStream(추상 구성요소)
- InputStream을 상속받는 요소들
- FileInputSystem, StringBufferInputStream, ByteArrayInputStream(데코레이터에 감싸이는 구상 구성 요소 역할)
- FilterInputStream(추상 데코레이터)
- PushbackInputStream, BufferedInputStream, DataInputStream.. etc(얘를 상속받는 구상 데코레이터들)