728x90
반응형
데코레이터 패턴(Decorator Pattern)
기존 클래스 코드를 변경하지 않고 객체에 새로운 작업을 추가할 수 있는 패턴
데코레이터 패턴을 사용하면 객체에 추가 요소를 동적으로 더할 수 있습니다.
또한, 서브클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있습니다.
- 데코레이터의 슈퍼클래스는 자신이 장식하고 있는 객체의 슈퍼클래스와 같습니다.
- 한 개체를 여러 개의 데코레이터로 감쌀 수 있습니다.
- 데코레이터는 자신이 감싸고 있는 객체와 같은 슈퍼클래스를 가지고 있기에, 원래 객체(싸여 있는 객체)가 들어갈 자리에 데코레이터 객체를 넣어도 무관합니다.
- 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 일 말고도 추가 작업을 수행할 수 있습니다.
- 객체는 언제든지 감쌀 수 있으므로 실행 중에 필요한 데코레이터를 마음대로 적용할 수 있습니다.
상속이 좋은 방법이긴 하지만, 서브클래스를 계속 만들게 되면, 모든 서브클래스에서 똑같은 행동을 상속받아야 되고, 컴파일시에 결정되기 때문에 실행시에 동적으로 이를 정의할 수가 없습니다.
🔑키 포인트는, 기존 코드를 건드리지 않고 기능을 새로 추가할 수 있다는 점입니다.
데코레이터 패턴 구조
- Component
- 구성요소(장식의 대상)
- ConcreteComponent
- 새로운 행동을 동적으로 추가
- Decorator
- 자신이 장식할 구성 요소와 같은 인터페이스 또는 추상 클래스를 구현
- ConcreteDecoratorA, ConcreteDecoratorB
- 데코레이터가 새로운 메소드를 추가할 수도 있습니다.
- 하지만 일반적으로 새로운 메소드를 추가하는 대신
Component
에 원래 있던 메소드를 별도의 작업으로 처리해서 새로운 기능을 추가합니다
OCP(Open Closed Principle)
클래스의 확장에는 열려 있어야 하지만 변경에는 닫혀 있어야 한다.!
모순처럼 보이지만, 직접 코드를 수정하지 않고도 코드를 확장할 수 있게 해주는 기법입니다.!
기존에 별다방 음료 애플리케이션은 다음과 같았습니다.
우와! 클래스가 폭발 했습니다.!! 🌋
이러한 상황에서 우유 가격이 상승한다면? 새로운 모카가 추가된가면??!
반응형
왜 클래스가 많이 필요하나요?
인스턴스 변수와 슈퍼클래스 상속을 이용해서 첨가물을 관리해볼까요?
첨가물에 대해서는 불리언 변수로 만들고, cost()
는 추상메소드로 정의하지 않고 구현했습니다.
하지만, 이런식으로 설계한다면 다음과 같은 문제가 발생할 수 있습니다.
- 첨가물 가격이 바뀔때 마다 기존 코드를 수정해야합니다.
- 첨가물의 종류가 많아지면 새로운 메소드를 추가해야합니다.
- 새로운 음료가 출시 되었습니다.! 그런데 그 음료는 첨가물이 없는 음료라면…? 필요없는 메소드를 상속 받게 됩니다…!
- 만약 동일한 첨가물을 계속 넣는다면?
데코레이터 패턴으로 ⭐️ 별다방 음료 메뉴 리팩토링
특정 음료에서 시작해서 첨가물로 그 음료를 장식하는 방식으로 구현해 봅시다.!
키포인트는 상속을 이용해서 클래스를 확장하고, 구성을 이용해 새로운 행동을 유연하게 추가합니다.
StarBeverage
public abstract class StarBeverage {
protected String description = "설명 없음";
public String getDescription() {
return description;
}
public abstract double cost();
}
Americano
public class Americano extends StarBeverage {
public Americano() {
description = "아메리카노";
}
@Override
public double cost() {
return 2500;
}
}
Espresso
public class Espresso extends StarBeverage {
public Espresso() {
description = "에스프레소";
}
@Override
public double cost() {
return 2000;
}
}
StarCondimentDecorator
/**
* extends StarBeverage 클래스를 확장 합니다.
*/
public abstract class StarCondimentDecorator extends StarBeverage {
/**
* StarBeverage 클래스 구성을 이용합니다.
*/
protected StarBeverage startBeverage;
public abstract String getDescription();
}
Ice
public class Ice extends StarCondimentDecorator {
public Ice(StarBeverage startBeverage) {
this.startBeverage = startBeverage;
}
@Override
public double cost() {
return this.startBeverage.cost() + 500;
}
@Override
public String getDescription() {
return this.startBeverage.getDescription() + ", 얼음";
}
}
Whip
public class Whip extends StarCondimentDecorator {
public Whip(StarBeverage starBeverage) {
this.startBeverage = starBeverage;
}
@Override
public double cost() {
return this.startBeverage.cost() + 300;
}
@Override
public String getDescription() {
return this.startBeverage.getDescription() + ", 휘핑 크림";
}
}
Client
public class Client {
public static void main(String[] args) {
StarBeverage beverage1 = new Espresso();
System.out.println("첫번째 주문 = "+beverage1.getDescription()+" :: 가격 = "+beverage1.cost());
StarBeverage beverage2 = new Americano();
beverage2 = new Ice(beverage2);
System.out.println("두번째 주문 = " + beverage2.getDescription()+" :: 가격 = "+beverage2.cost());
beverage2 = new Whip(beverage2);
System.out.println("세번째 주문 = " + beverage2.getDescription()+" :: 가격 = "+beverage2.cost());
}
}
결과
첫번째 주문 = 에스프레소 :: 가격 = 2000.0
두번째 주문 = 아메리카노, 얼음 :: 가격 = 3000.0
세번째 주문 = 아메리카노, 얼음, 휘핑 크림 :: 가격 = 3300.0
정리
- 디자인의 유연성 면에서 바라보면 상속으로 확장하는 일은 좋은 선택은 아님
- 구성과 위임으로 실행 중에 새로운 행동을 추가할 수 있음
- 상속 대신 데코레이터 패턴으로 행동을 확장할 수 있음
- 데코레이터 패턴은 구상 구성 요소를 감싸 주는 데코레이터를 사용
- 데코레이터 클래스의 형식은 그 클래스가 감싸는 클래스 형식을 반영
- 상속이나 인터페이스 구현으로 자신이 감쌀 클래스와 같은 타입
- 데코레이터는 자신이 감싸고 있는 구성 요소의 메소드를 호출한 결과에 새로운 기능을 더함으로써 행동을 확장
- 구성 요소를 감싸는 데코레이터의 갯수에는 제한이 없음
- 구성 요소의 클라이언트는 데코레이터의 존재를 알 수 없다.
- 다만, 클라이언트가 구성 요소의 구체적인 형식에 의존하는 경우는 예외
- 데코레이터 패턴을 사용하면 서브 클래스가 매우 많이 추가될 수 있음
- 데코레이터를 너무 많이 사용하면 코드가 필요 이상으로 복잡해짐
728x90
반응형
'컴퓨터과학 > 0 + 소프트웨어 아키텍처(디자인 패턴)' 카테고리의 다른 글
[소프트웨어 아키텍처] 16. 브릿지 패턴(Bridge Pattern -java) (0) | 2022.12.09 |
---|---|
[소프트웨어 아키텍처] 15. 어댑터 패턴, 퍼사드 패턴(Adapter Pattern, Facade Pattern -java) (1) | 2022.12.08 |
[소프트웨어 아키텍처] 13. 프로토타입 패턴(Prototype Pattern -java) (0) | 2022.12.07 |
[소프트웨어 아키텍처] 12. 빌더 패턴(Builder Pattern -java) (0) | 2022.12.06 |
[소프트웨어 아키텍처] 11. 싱글톤 패턴(Singleton Pattern -java) (0) | 2022.12.06 |