-
Notifications
You must be signed in to change notification settings - Fork 0
3주차 데코레이터 패턴 #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
3주차 데코레이터 패턴 #3
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package decorator; | ||
|
|
||
| import decorator.beverage.DarkRoast; | ||
| import decorator.beverage.Espresso; | ||
| import decorator.beverage.HouseBlend; | ||
| import decorator.beverage.base.Beverage; | ||
| import decorator.beverage.enums.Size; | ||
| import decorator.decorator.Mocha; | ||
| import decorator.decorator.Soy; | ||
| import decorator.decorator.Whip; | ||
|
|
||
| public class DecoratorPattern { | ||
| public static void main(String[] args) { | ||
| decorating(); | ||
| decoratingWithSize(); | ||
| } | ||
|
|
||
| private static void decorating() { | ||
| final Beverage espresso = new Espresso(); | ||
| printBeverage(espresso); | ||
|
|
||
| Beverage darkRoast = new DarkRoast(); | ||
| printBeverage(darkRoast); // 일반 다크 로스트 커피 | ||
|
|
||
| darkRoast = new Mocha(darkRoast); // mocha로 감싸기 | ||
| darkRoast = new Mocha(darkRoast); // mocha로 한 번 더 감싸기 | ||
| darkRoast = new Whip(darkRoast); // 휘핑크림 추가 | ||
| printBeverage(darkRoast); // 모카샷2 + 휘핑크림 추가한 다크 로스트 커피 | ||
|
|
||
| Beverage houseBlend = new HouseBlend(); | ||
| printBeverage(houseBlend); // 일반 하우스 블랜드 커피 | ||
|
|
||
| houseBlend = new Soy(houseBlend); | ||
| houseBlend = new Mocha(houseBlend); | ||
| houseBlend = new Whip(houseBlend); | ||
| printBeverage(houseBlend); // 두유 + 모카샷 + 휘핑크림 추가한 블랜드 커피 | ||
| } | ||
|
|
||
| private static void decoratingWithSize() { | ||
| Beverage darkRoast = new DarkRoast(); | ||
| printBeverage(darkRoast); // 톨 사이즈의 다크 로스트 커피 | ||
|
|
||
| darkRoast.setSize(Size.VENTI); | ||
| printBeverage(darkRoast); // 벤티 사이즈의 다크 로스트 커피 | ||
|
|
||
| darkRoast = new Mocha(darkRoast); // mocha로 감싸기 | ||
| darkRoast = new Mocha(darkRoast); // mocha로 한 번 더 감싸기 | ||
| darkRoast = new Whip(darkRoast); // 휘핑크림 추가 | ||
| printBeverage(darkRoast); // 모카샷2 + 휘핑크림 추가한 벤티 사이즈의 다크 로스트 커피 | ||
| } | ||
|
|
||
| private static void printBeverage(Beverage beverage) { | ||
| System.out.println(beverage.getSize() + ": " + beverage.getDescription() + " $" + beverage.cost()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package decorator.beverage; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
|
|
||
| public class DarkRoast extends Beverage { | ||
|
|
||
| public DarkRoast() { | ||
| description = "다크 로스트 커피"; // description 변수는 Beverage로부터 상속받음 | ||
| } | ||
|
|
||
| @Override | ||
| public double cost() { | ||
| return 0.99 * sizeCostMap.get(super.getSize()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package decorator.beverage; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
|
|
||
| public class Decaf extends Beverage { | ||
|
|
||
| public Decaf() { | ||
| description = "디카페인 커피"; // description 변수는 Beverage로부터 상속받음 | ||
| } | ||
|
|
||
| @Override | ||
| public double cost() { | ||
| return 0.89 * sizeCostMap.get(super.getSize()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package decorator.beverage; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
|
|
||
| public class Espresso extends Beverage { | ||
|
|
||
| public Espresso() { | ||
| description = "에스프레소"; // description 변수는 Beverage로부터 상속받음 | ||
| } | ||
|
|
||
| @Override | ||
| public double cost() { | ||
| return 1.99 * sizeCostMap.get(super.getSize()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package decorator.beverage; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
|
|
||
| public class HouseBlend extends Beverage { | ||
|
|
||
| public HouseBlend() { | ||
| description = "하우스 블렌드 커피"; // description 변수는 Beverage로부터 상속받음 | ||
| } | ||
|
|
||
| @Override | ||
| public double cost() { | ||
| return 0.89 * sizeCostMap.get(super.getSize()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package decorator.beverage.base; | ||
|
|
||
| import decorator.beverage.enums.Size; | ||
|
|
||
| import java.util.EnumMap; | ||
|
|
||
| public abstract class Beverage { | ||
|
|
||
| protected static final EnumMap<Size, Double> sizeCostMap; | ||
|
|
||
| static { | ||
| sizeCostMap = new EnumMap<>(Size.class); | ||
| sizeCostMap.put(Size.TALL, 1.0); | ||
| sizeCostMap.put(Size.GRANDE, 1.2); | ||
| sizeCostMap.put(Size.VENTI, 1.5); | ||
| } | ||
|
|
||
| protected Size size = Size.TALL; | ||
| protected String description = "제목 없음"; | ||
|
|
||
| // 추상 클래스에서 getDescription() 을 미리 구현됨. | ||
| public String getDescription() { | ||
| return description; | ||
| } | ||
|
|
||
| public Size getSize() { | ||
| return size; | ||
| } | ||
|
|
||
| public void setSize(Size size) { | ||
| this.size = size; | ||
| } | ||
|
|
||
| // cost() 는 서브 클래스에서 구현해야 함. | ||
| public abstract double cost(); | ||
| } | ||
|
Comment on lines
+7
to
+36
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Beverage를 추상 클래스 또는 인터페이스로 정의하여 데코레이터 패턴을 활용할 수 있음 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package decorator.beverage.enums; | ||
|
|
||
| public enum Size { | ||
| TALL, GRANDE, VENTI | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package decorator.decorator; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
| import decorator.decorator.base.CondimentDecorator; | ||
|
|
||
| public class Mocha extends CondimentDecorator { | ||
| public Mocha(Beverage beverage) { | ||
| this.beverage = beverage; | ||
| } | ||
|
|
||
| @Override | ||
| public double cost() { | ||
| // 장식하고 있는 객체(beverage)에 cost() 작업을 위임하여 리턴값을 구한뒤 | ||
| // 그 결과에 모카 가격을 사이즈게 맞게 추가로 계산 | ||
| return beverage.cost() + 0.35; | ||
| } | ||
|
|
||
| @Override | ||
| public String getDescription() { | ||
| // 장식하고 있는 객체(beverage)에 대한 설명에 | ||
| // 그 결과에 모카에 대한 설명을 추가로 붙힘 | ||
| return beverage.getDescription() + ", 모카"; | ||
| } | ||
| } | ||
|
Comment on lines
+6
to
+24
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 구상 클래스는 구상 요소에 맞게 새로운 기능/데이터를 더하여 행동을 확장한다.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package decorator.decorator; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
| import decorator.decorator.base.CondimentDecorator; | ||
|
|
||
| public class Soy extends CondimentDecorator { | ||
|
|
||
| public Soy(Beverage beverage) { | ||
| this.beverage = beverage; | ||
| } | ||
|
|
||
| @Override | ||
| public double cost() { | ||
| // 장식하고 있는 객체(beverage)에 cost() 작업을 위임하여 리턴값을 구한뒤 | ||
| // 그 결과에 두유 가격을 사이즈게 맞게 추가로 계산 | ||
| return beverage.cost() + 0.11; | ||
| } | ||
|
|
||
| @Override | ||
| public String getDescription() { | ||
| // 장식하고 있는 객체(beverage)에 대한 설명에 | ||
| // 그 결과에 두유에 대한 설명을 추가로 붙힘 | ||
| return beverage.getDescription() + ", 두유"; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package decorator.decorator; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
| import decorator.decorator.base.CondimentDecorator; | ||
|
|
||
| public class Whip extends CondimentDecorator { | ||
|
|
||
| public Whip(Beverage beverage) { | ||
| this.beverage = beverage; | ||
| } | ||
|
|
||
| @Override | ||
| public double cost() { | ||
| // 장식하고 있는 객체(beverage)에 cost() 작업을 위임하여 리턴값을 구한뒤 | ||
| // 그 결과에 휘핑크림 가격을 사이즈게 맞게 추가로 계산 | ||
| return beverage.cost() + 0.15; | ||
| } | ||
|
|
||
| @Override | ||
| public String getDescription() { | ||
| // 장식하고 있는 객체(beverage)에 대한 설명에 | ||
| // 그 결과에 휘핑크림에 대한 설명을 추가로 붙힘 | ||
| return beverage.getDescription() + ", 휘핑크림"; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package decorator.decorator.base; | ||
|
|
||
| import decorator.beverage.base.Beverage; | ||
| import decorator.beverage.enums.Size; | ||
|
|
||
| /** | ||
| * 첨가물 | ||
| */ | ||
| public abstract class CondimentDecorator extends Beverage { | ||
| /** | ||
| * 각 데코레이터가 감쌀 음료를 나타내는 Beverage 객체를 저장할 인스턴스 변수. | ||
| * 모든 Beverage의 구상 클래스를 담기 위해 "Beverage"라는 슈퍼클래스 유형을 사용한다. | ||
| * <p> | ||
| * Wrapping 하여 데코레이팅한다. | ||
| */ | ||
| protected Beverage beverage; | ||
|
|
||
| @Override | ||
| public abstract String getDescription(); | ||
|
|
||
| @Override | ||
| public Size getSize() { | ||
| return beverage.getSize(); | ||
| } | ||
| } | ||
|
Comment on lines
+6
to
+25
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
헤드퍼스트 디자인패턴 예시에는 "상속"으로 형식을 동일하게 하였다. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # 상속의 문제점 | ||
|
|
||
| > concrete class가 너무 많아짐 | ||
| > | ||
|
|
||
| 스타벅스 커피 전문점의 음료를 나타내는 추상 클래스 : Beverage를 두고 | ||
| 메뉴가 추가될때마다 서브 클래스를 추가하게 되면 클래스가 겉잡을 수 없이 많아진다. | ||
|
|
||
| > 컴파일 시점에 행동이 정적으로 정해짐 | ||
| > | ||
|
|
||
| # 디자안 원칙 :OCP | ||
|
|
||
| **Open-Closed Principle** | ||
|
|
||
| > 클래스의 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다. | ||
|
|
||
| 기존 코드를 건드리지 않고 확장이 가능할까? -> observer 패턴 | ||
|
|
||
| # 데코레이터 패턴 | ||
|
|
||
|  | ||
|  | ||
|
|
||
| > 객체에 추가 요소를 동적으로 더할 수 있는 패턴으로, 서브클래스를 만들 때보다 유연하게 기능읗 확장할 수 있는 특징이 있다. | ||
|
|
||
| - 데코레이터의 슈퍼 클래스는 자신이 장식하고 있는 객체의 슈퍼클래스와 동일하다 | ||
| - 그렇기 때문에, wrapping된 객체가 들어갈 자리에 데코레이터 객체를 넣을 수 있다 | ||
| - 하나의 객체를 여러 데이코레이터로 감쌀(Wrapping) 수 있다 | ||
| - **데코레이터는 객체에 행동을 위임하는 일과 함께 추가 작업을 수행할 수 있다** (키 포인트) | ||
|
|
||
| ## 데코레이터 패턴 예제에서 "상속"이 사용되는 이유 | ||
|
|
||
| 데코레이터의 특징은 "데코레이터로 감싸는 객체"의 형식과 "데코레이터"의 형식이 같다는 것이다. | ||
| 이 형식을 "상속"으로서 맞추는 것이다. (도구로서 상속이 활용 됨) | ||
|
|
||
| - 상속은 행동이 컴파일 시점에 정해져버리지만, 데코레이터 패턴은 구성을 활용하여 실행중에 조합해서 사용할 수 있다. | ||
|
|
||
| 또한 기존 코드에서 Beverage 라는 추상 클래스를 사용하고 있기 때문에 기존 코드를 고치지 않고 "상속"으로 형식을 맞추었다. | ||
|
|
||
| - 상속 대신 "인터페이스"로 형식을 맞출 수 있다. | ||
|
|
||
| ## 데코레이터 패턴의 예시 : 자바 I/O | ||
|
|
||
|  |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
객체를 여러 번 감싸므로써 행동을 위임하여 새로운 기능/데이터를 더한다.