[Design Pattern] Decorator
14853 단어 KDT과제Design PatternDesign Pattern
의미
- 특정 클래스의 객체들이 할 수 있는 일들을 여러가지 두고, 각 객체마다 사용자가 원하는대로 골라 시키거나 기능들을 필요에 따라 장착하게 할 때 사용 (참고: https://www.youtube.com/watch?v=q3_WXP9pPUQ)
문제 상황
알림 관련 라이브러리를 생각해보자. 처음에는 알림 클래스가 간단하게 구성되어 있을 것이다. 해당 알림 클래스는 중요한 이벤트가 발생할 때마다 사용자에게 알림을 보내는 역할을 한다.
하지만 나중에 facebook이나 slack 같은 어플리케이션에 알림을 보내려고 하면, 추가로 다른 알림 클래스를 생성하여, 기존 클래스를 상속받아야 한다.
그리고 한 번에 여러 어플리케이션에 알림을 보내려고 하면 각 알림 클래스들을 결합시킨 클래스를 새로 생성해야한다.
해결 방안
- 상속 대신 집합(Aggregation)이나 합성(Composition)을 이용하기
- 상속의 단점
- static해서 runtime에 코드 수정을 할 수 없다.
- 여러 클래스를 동시에 상속받을 수 없다.
cf) 집합 -> 합성 -> 상속 순으로 갈 수록 클래스 간의 결합도가 커진다.
- Wrapper 클래스 사용
- Wrapper 클래스란?
- 기존 객체를 감싸고, 새로운 기능을 추가시킬 수 있는 클래스
- 밑의 그림에서는
BaseDecorator
가 Wrapper 클래스
구조
예시 및 구현 방법
- 커피에 여러가지 토핑을 추가시키는 코드를 예시로 작성해보자.
- 어떤 메소드가 공통된 행위(예: 토핑 추가)를 가지는지 식별한 후, 해당 메소드를
Component
인터페이스로 선언한다.
// Component
public interface Coffee {
void addTopping();
}
ConcreteComponent
클래스(Americano
)를 생성하고,Component
인터페이스(Coffee
)를 상속받아 공통된 행위를 클래스에 맞게 구현한다.ConcreteComponent
: wrapped 될 객체의 클래스
// Concrete Component
public class Americano implements Coffee {
@Override
public void addTopping() {
System.out.println("아메리카노 추가");
}
}
BaseDecorator
클래스(CoffeeDecorator
)를 생성하고, wrapped된 객체의 참조를 저장하는private
필드(보통wrappee
라 함)를 생성한다. 해당 필드는ConcreteComponent
와 연결될 수 있도록Component
인터페이스 타입으로 생성되어야 한다. 그 후,Component
인터페이스의 메소드를 모두 구현한다.
// Base Decorator
public class CoffeeDecorator implements Coffee {
private final Coffee wrappee;
CoffeeDecorator(Coffee coffee) {
this.wrappee = coffee;
}
@Override
public void addTopping() {
wrappee.addTopping();
}
}
BaseDecorator
클래스를 상속받는ConcreteDecorator
클래스(JavaChipDecorator
,CoffeeDecorator
,MilkDecorator
)를 생성한다.
// Concrete Decorator
public class JavaChipDecorator extends CoffeeDecorator{
public JavaChipDecorator(Coffee coffee) {
super(coffee);
}
@Override
public void addTopping() {
super.addTopping();
System.out.println("자바칩 추가");
}
}
public class MilkDecorator extends CoffeeDecorator{
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public void addTopping() {
super.addTopping();
System.out.println("우유 추가");
}
}
public class WhippingCreamDecorator extends CoffeeDecorator{
public WhippingCreamDecorator(Coffee coffee) {
super(coffee);
}
@Override
public void addTopping() {
super.addTopping();
System.out.println("휘핑크림 추가");
}
}
- 객체 생성하는 코드를 덧 씌우는 방식으로
Client
코드를 작성한다.
public class Client {
public static void main(String[] args) {
// 아메리카노 추가
new Americano().addTopping();
// 아메리카노 추가
// 우유 추가
new MilkDecorator(
new Americano()
).addTopping();
// 아메리카노 추가
// 우유 추가
// 자바칩 추가
// 휘핑크림 추가
new WhippingCreamDecorator(
new JavaChipDecorator(
new MilkDecorator(
new Americano()
)
)
).addTopping();
}
}
장단점
- 장점
- 새로운 자식클래스를 만들지 않고도 객체의 행위를 상속시킬 수 있다.
- runtime에 객체의 책임을 더하거나 제거할 수 있다.
- 상속말고 집합이나 합성을 써서 얻게 된 이점
- 다수의 decorator로 객체를 감싸서 여러 행위를 조합할 수 있다.
- SRP
- 단점
- 자잘한 객체들이 많이 생길 수 있다.
참고
Author And Source
이 문제에 관하여([Design Pattern] Decorator), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@iyj6707/Design-Pattern-Decorator저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)