From 9f331baad2afe462a97f605edd378952d8731814 Mon Sep 17 00:00:00 2001 From: Nari Jeong <92130993+naringst@users.noreply.github.com> Date: Tue, 10 Mar 2026 19:08:20 +0900 Subject: [PATCH 1/2] =?UTF-8?q?docs:=203=EC=9E=A5=20=EB=8D=B0=EC=BD=94?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=ED=84=B0=20=ED=8C=A8=ED=84=B4=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...64\355\204\260\355\214\250\355\204\264.md" | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 "nari/3.\353\215\260\354\275\224\353\240\210\354\235\264\355\204\260\355\214\250\355\204\264.md" diff --git "a/nari/3.\353\215\260\354\275\224\353\240\210\354\235\264\355\204\260\355\214\250\355\204\264.md" "b/nari/3.\353\215\260\354\275\224\353\240\210\354\235\264\355\204\260\355\214\250\355\204\264.md" new file mode 100644 index 0000000..9bd0e66 --- /dev/null +++ "b/nari/3.\353\215\260\354\275\224\353\240\210\354\235\264\355\204\260\355\214\250\355\204\264.md" @@ -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(얘를 상속받는 구상 데코레이터들) From 31cb654ef5f9cf28c6dce59960a93f73bd8e51cf Mon Sep 17 00:00:00 2001 From: Nari Jeong <92130993+naringst@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:57:56 +0900 Subject: [PATCH 2/2] =?UTF-8?q?docs:=204=EC=9E=A5=20=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=ED=8C=A8=ED=84=B4=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...40\353\246\254\355\214\250\355\204\264.md" | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 "nari/04.\355\214\251\355\206\240\353\246\254\355\214\250\355\204\264.md" diff --git "a/nari/04.\355\214\251\355\206\240\353\246\254\355\214\250\355\204\264.md" "b/nari/04.\355\214\251\355\206\240\353\246\254\355\214\250\355\204\264.md" new file mode 100644 index 0000000..e8393e3 --- /dev/null +++ "b/nari/04.\355\214\251\355\206\240\353\246\254\355\214\250\355\204\264.md" @@ -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의 서브클래스가 그 호출을 받아 피자를 만듦 -> 가게에 따라 다름 +- 피자의 종류는 어떤 서브클래스를 선택했느냐에 따라 결정됨 + +# 팩토리 메소드 선언하기 + +- 구상 클래스 인스턴스 만드는 일을 하나의 객체가 전부 처리하는 방식에서 서브 클래스가 처리하는 방식으로 바뀌었음 + +# 팩토리 메소드 패턴 살펴보기 + +- 모든 팩토리 패턴은 객체 생성을 캡슐화 +- 팩토리 메소드 패턴은 서브클래스에서 어떤 클래스를 만들지 결정함으로써 객체 생성을 캡슐화 + +# 팩토리 메소드 패턴의 정의 + +- 팩토리 메소드 패턴에서는 객체를 생성할 때 필요한 인터페이스를 만듦. +- 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정. +- 팩토리 메소드 패턴을 사용하면 클래스 인스턴스 만드는 일을 서브클래스에게 맡김 + => 사용하는 서브클래스에 따라 생산되는 객체 인스턴스가 결정 + +# 객체 의존성 살펴보기 + +- 객체 인스턴스를 직접 만들면 구상 클래스에 의존 + +# 의존성 뒤집기 원칙 + +- 추상화된 것에 의존하게 만들고 구상 클래스에 의존하지 않게 만든다 +- 항상 추상화에 의존하게 만들어야 함!