데코레이터 패턴(decorator) ?
: 특정 클래스의 객체들이 할수 있는 일을 여러가지 두고, 각 객체마다 사용자가 원하는대로 골라 시키거나 기능들을 필요에 따라 장착할 때 데코레이션 패턴이 사용됨.
가정)
자바커피는 엄청난 급속도로 성장한 초대형 커피 전문점으로 유명합니다.
가정)
커피를 주문할 때는 스팀 우유나 두유, 모카를 추가하고 휘핑크림을 얹기도 합니다.(취향에 따라)
각각을 추가할 때마다 커피 가격도 달라지기 때문에, 주문시스템에서도 그런 점들을 모두 고려해야합니다.
문제점?
- 첨가물 각겨이 바뀔때마다 기존 코드를 수정해야합니다.
- 첨가물의 종류가 많아지면 새로운 메소드를 추가해야 하고, 수퍼클래스와 가격()도 고쳐야합니다.
- 새로운 음료가 출시 될 수도 있습니다. 그 중에는 특정 첨가물이 들어가면 안되는 경우도 있을 것입니다.
(예를들어, 꿀차 같은서브클래스에서도 hasWhip() -> 휘핑크림 같은 메소드는 여전히 상속 받게 될 것입니다.)
- 손님이 더블모카를 주문하면 어떻게 해야할까요?
디자인 원칙 : 클래스는 확장에 대해서는 열려 있어야하지만, 코드 변경에 대해서는 닫혀 있어야 한다.
가정) 손님이 카페라떼에 모카와 휘핑크림을 추가로 주문할 시 데코레이터 패턴 사용 방법
1. 카페라떼 객체를 가져온다.
2. 모카 객체로 장식(decorator)한다.
3 휘핑 객체로 장식(decorator)한다.
4. 가격() 메소드를 호출한다. (이때, 첨가물의 가격을 계산하는 일은 해당 각각 객체들에게 위임된다.)
5. 가격을 계산할 때, 휘핌크림의 가격()를 호출하면 됩니다. 그러면 휘핑크림에서는 그 객체가 장식하고 객체한테 가격 계산을 위임합니다.
데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 첨가한다.
데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
.
.
.
구현
//음료 클래스
public abstract class Beverage {
String description = "제목없음";
public String getDescription() {
return description;
}
//상속하는 서브클래스에서 정의
public abstract double cost();
}
//카페라떼
public class CafeRatte extends Beverage {
public CafeRatte() {
description = "카페라떼";
}
@Override
public double cost() {
return 3700;
}
}
//음료 클래스를 상속하는 첨가물(휘핑크림 등)클래스
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
//휘핑크림
public class Whip extends CondimentDecorator{
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1000 + beverage.cost();
}
@Override
public String getDescription() {
return beverage.getDescription() +" 휘핑크림";
}
}
//첨가물 중 하나인 모카
public class Moca extends CondimentDecorator{
//감싸고자 하는 음료를 저장하기 위한 인스턴스 변수
Beverage beverage;
public Moca(Beverage beverage) {
// 인스턴스 변수를 감싸고자하는 객체로 설정하기 위한 생성자
this.beverage = beverage;
}
@Override
public double cost() {
return 500 + beverage.cost();
}
@Override
public String getDescription() {
return beverage.getDescription() + " 모카";
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Beverage beverage = new CafeRatte();
//감싸기
beverage = new Whip(beverage);
//감싸기
beverage = new Moca(beverage);
Log.d("abcd","결과 : "+beverage.cost());
}
}
결과 : 5200.0
코틀린 구현
//음료 클래스
abstract class Beverage(open val description:String = "") {
abstract fun cost() : Double
}
//카페라떼
class CaffeRatte : Beverage() {
override val description: String
get() = super.description + " 카페라떼"
override fun cost(): Double {
return 4000.0
}
}
//첨가물 클래스
abstract class CondimentDecorator : Beverage() {
override val description: String
get() = super.description
}
//모카 클래스
class Moca constructor(val beverage: Beverage) : CondimentDecorator(){
override val description: String
get() = "${beverage.description}, 모카"
override fun cost(): Double {
return beverage.cost() + 500.0
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var beverage:Beverage = CaffeRatte()
beverage = Moca(beverage)
Log.d("abcd", "결과 : "+ beverage.description)
}
}
결과 : 카페라떼, 모카
'개발공부 > 디자인 패턴' 카테고리의 다른 글
자바 및 코틀린 추상 팩토리(factory)패턴이란? (0) | 2022.01.19 |
---|---|
자바 및 코틀린 팩토리(factory)패턴이란? (0) | 2022.01.15 |
자바 및 코틀린 객체지향패턴 (Observer 패턴) (0) | 2022.01.06 |
자바 및 코틀린 객체지향패턴 (위임, 인터페이스 사용법) (0) | 2022.01.03 |
책 이름 : Head First Design Pattern (0) | 2022.01.03 |