우리가 마라탕 식당에서 다양한 재료들을 넣어 주문을 한다.
예를 들어 주꾸미가 들어간 마라탕, 양고기와 수많은 야채가 들어간 마라탕 등 이렇게 주문을 한다고 생각해보자.
종업원 측에서는 메뉴판을 구성하기 위한 비용이 많이 들 수 밖에 없다. (메뉴판을 만들다가 밤 샐 수도 있다.)
그렇다고 우리는 재료를 접시에 담아 주문을 하지, 저렇게 긴 이름으로 주문을 하지 않는다.
그래서 들어 있는 재료의 무게 및 가격을 책정하여 가격을 결정한다.
이와 같이 다양한 요소들을 하나의 부속물로 치고 상속 시키는 개념이 Decorator 패턴 이다.
// Component 구현 : 마라 요리 클래스
public abstract class MaraDish {
private String description;
public MaraDish() {
this.description = "-- 요리 재료 목록 --\n";
}
public String getDescription() {
return description;
}
public abstract int getPrice();
}
MaraDish.java
// ConcreteComponent 구현 (1) : 마라탕
public class MaraSoup extends MaraDish {
private int price;
public MaraSoup() {
this.price = 8000;
}
public String getDescription() {
return "[[마라탕 주문]]\n마라탕\t\t8000 원\n";
}
public int getPrice() {
return price;
}
}
MaraSoup.java
// ConcreteComponent 구현 (2) : 마라샹궈
public class MaraChangguo extends MaraDish {
private int price;
public MaraChangguo() {
this.price = 16000;
}
public String getDescription() {
return "[[마라샹궈 주문]]\n마라샹궈\t\t16000 원\n";
}
public int getPrice() {
return price;
}
}
MaraChangguo.java
// Decorator 구현 : 요리 재료
public abstract class Ingredient extends MaraDish {
public abstract String getDescription();
}
Ingredient.java
// Concrete Decorator (1) 구현 : 어류 재료
public class Fish extends Ingredient {
private MaraDish maraDish;
private FishType fishType; // Enumeration Type
private int exp; // N 마리
public Fish(MaraDish maraDish, FishType fishType, int exp) {
this.maraDish = maraDish;
this.fishType = fishType;
this.exp = exp;
}
public String getDescription() {
return maraDish.getDescription() + String.format("%s(%d 개)\t\t%d 원\n", this.fishType.getValue(), this.exp, this.exp * 1000);
}
public int getPrice() {
return this.exp * 1000 + this.maraDish.getPrice();
}
}
Fish.java
// Concrete Decorator (2) 구현 : 육류 재료
public class Meat extends Ingredient {
private MaraDish maraDish;
private MeatType meatType; // Enumeration Type
private int weight; // 그램 단위
public Meat(MaraDish maraDish, MeatType meatType, int weight) {
this.maraDish = maraDish;
this.meatType = meatType;
this.weight = weight;
}
public String getDescription() {
return this.maraDish.getDescription() + String.format("%s(%d g)\t\t%d 원\n", this.meatType.getValue(), this.weight, this.weight / 100 * 1000);
}
public int getPrice() {
return this.weight / 100 * 1000 + this.maraDish.getPrice();
}
}
Meat.java
// Concrete Decorator (3) 구현 : 사리 재료
public class Noodle extends Ingredient {
private MaraDish maraDish;
private NoodleType noodleType; // Enumeration Type
private int exp; // N 인분
public Noodle(MaraDish maraDish, NoodleType noodleType, int exp) {
this.maraDish = maraDish;
this.noodleType = noodleType;
this.exp = exp;
}
public String getDescription() {
if (this.noodleType.equals(NoodleType.RAMEN)) {
return this.maraDish.getDescription() + String.format("%s(%d 인분)\t\t%d 원\n", this.noodleType.getValue(), this.exp, this.exp * 1000);
} else {
return this.maraDish.getDescription() + String.format("%s(%d 인분)\t\t%d 원\n", this.noodleType.getValue(), this.exp, this.exp * 2000);
}
}
public int getPrice() {
// 라면 사리는 1인분에 1000 원으로 책정한다.
if (this.noodleType.equals(NoodleType.RAMEN)) {
return 1000 * this.exp + this.maraDish.getPrice();
} else {
return 2000 * this.exp + this.maraDish.getPrice();
}
}
}
Noodle.java
// Concrete Decorator (4) 구현 : 채소 재료
public class Vegetable extends Ingredient {
private MaraDish maraDish;
private VegetableType vegetableType; // Enumeration Type
private int weight; // 그램 단위
public Vegetable(MaraDish maraDish, VegetableType vegetableType, int weight) {
this.maraDish = maraDish;
this.vegetableType = vegetableType;
this.weight = weight;
}
public String getDescription() {
return this.maraDish.getDescription() + String.format("%s(%d g)\t\t%d 원\n", this.vegetableType.getValue(), this.weight, this.weight / 25 * 250);
}
public int getPrice() {
return this.weight / 25 * 250 + this.maraDish.getPrice();
}
}
Vegetable.java
// Enumeration 의 값을 얻기 위한 인터페이스
public interface EnumModel {
String getKey();
String getValue();
}
EnumModel.java
public enum FishType implements EnumModel {
SQUID("오징어"), OCTOPUS("주꾸미"), SHRIMP("새우"), SKEWERS("꼬치");
private String value;
FishType(String value) {
this.value = value;
}
@Override
public String getKey() {
return name();
}
@Override
public String getValue() {
return value;
}
}
FishType.java
public enum MeatType implements EnumModel {
PORK("돼지고기"), BEEF("소고기"), LAMB("양고기"), HAM("햄"), SAUSAGE("소세지");
private String value;
MeatType(String value) {
this.value = value;
}
@Override
public String getKey() {
return name();
}
@Override
public String getValue() {
return value;
}
}
MeatType.java
public enum NoodleType implements EnumModel {
RAMEN("라면사리"), NOODLE("중국소면"), SUJEBI("수제비"), RICE_CAKE("떡볶이떡");
private String value;
NoodleType(String value) {
this.value = value;
}
@Override
public String getKey() {
return name();
}
@Override
public String getValue() {
return value;
}
}
NoodleType.java
public enum VegetableType implements EnumModel {
LOTUS_ROOT("연근"), PUMPKIN("호박"), CORIANDER("고수"), SPROUTS("숙주나물"), MUSHROOM("버섯"), BOK_CHOY("청경채"), TOFU("두부");
private String value;
VegetableType(String value) {
this.value = value;
}
@Override
public String getKey() {
return name();
}
@Override
public String getValue() {
return value;
}
}
VegetableType.java
public class Main {
public static void main(String[] args) {
MaraDish maraSoup = new MaraSoup();
maraSoup = new Fish(maraSoup, FishType.OCTOPUS, 1);
maraSoup = new Meat(maraSoup, MeatType.BEEF, 100);
maraSoup = new Noodle(maraSoup, NoodleType.SUJEBI, 1);
maraSoup = new Noodle(maraSoup, NoodleType.RAMEN, 1);
maraSoup = new Vegetable(maraSoup, VegetableType.BOK_CHOY, 25);
maraSoup = new Vegetable(maraSoup, VegetableType.MUSHROOM, 25);
maraSoup = new Vegetable(maraSoup, VegetableType.PUMPKIN, 25);
maraSoup = new Vegetable(maraSoup, VegetableType.SPROUTS, 25);
System.out.println(maraSoup.getDescription());
System.out.println("마라탕 가격 : " + maraSoup.getPrice() + " 원\n");
MaraDish maraChangguo = new MaraChangguo();
maraChangguo = new Fish(maraChangguo, FishType.SQUID, 1);
maraChangguo = new Meat(maraChangguo, MeatType.LAMB, 100);
maraChangguo = new Noodle(maraChangguo, NoodleType.RICE_CAKE, 1);
maraChangguo = new Vegetable(maraChangguo, VegetableType.SPROUTS, 25);
maraChangguo = new Vegetable(maraChangguo, VegetableType.CORIANDER, 25);
maraChangguo = new Vegetable(maraChangguo, VegetableType.BOK_CHOY, 25);
System.out.println(maraChangguo.getDescription());
System.out.println("마라샹궈 가격 : " + maraChangguo.getPrice() + " 원");
}
}
Main.java
[[마라탕 주문]]
마라탕 8000 원
주꾸미(1 개) 1000 원
소고기(100 g) 1000 원
수제비(1 인분) 2000 원
라면사리(1 인분) 1000 원
청경채(25 g) 250 원
버섯(25 g) 250 원
호박(25 g) 250 원
숙주나물(25 g) 250 원
마라탕 가격 : 14000 원
[[마라샹궈 주문]]
마라샹궈 16000 원
오징어(1 개) 1000 원
양고기(100 g) 1000 원
떡볶이떡(1 인분) 2000 원
숙주나물(25 g) 250 원
고수(25 g) 250 원
청경채(25 g) 250 원
마라샹궈 가격 : 20750 원
실행 결과
@공통함수_이름
을 쓰면 데코레이션 기능을 사용할 수 있다.