티스토리 뷰
1. 데코레이터 패턴이란?
데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 첨가한다.
데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
구성요소
1. Component
- 동적으로 기능을 추가 받을 객체의 인터페이스
2. Decorator
- Component 객체에 대한 참조를 가진다.
- Component의 인터페이스 타입을 구현한다. (같은 타입을 가진다)
3. ConcreteComponent
- 기능이 추가 받을 수는 객체
4. ConcreteDecorator
- Component에 기능을 더한다.
기본이 되는 디자인 원칙
OCP(Open-Closed Principle): 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다.
2. 동기
클래스/인터페이스가 아닌 특정 객체에만 기능을 추가하고 싶을 때가 있다.
즉 기능 추가라는 확장은 하면서 기존 구조 (클래스)를 변경하는 일을 피하고 싶을 때 쓸 수 있는 디자인 패턴이다.
장점:
1) 정적 상속보다 더 유연하다.
- 그냥 상속을 하면 상위 클래스의 모든 것을 이어 받게 된다. 데코레이터를 쓰면 런타임에서 기능을 추가/분리를 할 수 있게 된다.
- 상속을 사용하게 되면 추가되는 기능마다 경우의 수가 늘어난다. 하지만 데코레이터는 기능 하나 당 한 클래스만 필요하다.
2) 필요한 기능 추가를 뒤로 미룰 수 있다.
- 추가할 기능과 그 기능을 가질 객체들이 분리가 되어있기 때문에, 상위 계층의 클래스 설계 시 어떤 기능이 필요할지 미리 생각할 필요가 없다.
단점:
1) 객체에 대한 비교를 할 수 없다.
- decorator로 component를 감싸는 순간, 더 이상 기능이 추가되기 전 객체가 아니게 된다.
- 따라서 equals 등 객체를 비교하는 로직을 사용할 수 없게 된다.
2) 자잘한 객체들이 많이 생겨 구조가 복잡해진다.
- 비슷하게 생긴 객체들이 많아진다.
- 여러 객체들이 인스턴스 변수나 어떤 값으로 정의되는게 아니라 참조로 인해 정의되기 때문에 처음보는 사람은 이해하기 매우 어려운 구조가 될 수 있다.
3. 적용 시 추가적으로 고려해야 될 것들
1) Component 클래스는 최대한 가볍게
- Component에 인스턴스 변수를 가지는 걸 피하는 것이 좋다.
- Component은 인터페이스를 정의하는 것에 초점을 두는 것이지, 데이터를 저장하는 역할을 하는 것이 아니다.
- 데이터를 저장하는 행위는 결국 기능과 연관이 되어있기 때문에 그 기능은 ConcreteDecorator들한테 위임하자.
2) 가능하다면 바로 ConcreteDecorator 사용
- 추가될 기능이 한가지라면 굳이 Decorator -> ConcreteDecorator 형식을 지킬 필요가 없다.
- 이런 경우는 주로 이미 클래스끼리의 계층구조가 정의되어 있을 때가 많다.
3) 데코레이터 VS 스트래티지
- 데코레이터는 껍데기를 바꾸는 것 VS 스트래티지는 알맹이를 바꾸는 것
4. 개인적인 포인트
1) 클래스가 아닌 객체를 타게팅해서 추가적인 작업을 하고 싶을 때 <- 이게 포인트 같다.
2) Gof Design Patterns 책에서 데코레이터의 장점 중 기능을 런타임에 탈부착(위에서는 추가/분리라고 함)할 수 있다고 했다.
새로운 기능을 추가하는 거는 데코레이터 클래스로 감싸면 되는 건 알겠는데, 분리는 어떻게 하는 것인가?
해결책을 찾아보니 되게 별로인데, 요약하자면 기존의 기능을 삭제하는 Decorator를 Wrapping 하는 것이다...
익숙한 커피 예시를 들어보면:
interface Coffee {
double cost();
String description();
}
class SimpleCoffee implements Coffee {
public double cost() {
return 2.0;
}
public String description() {
return "Simple Coffee";
}
}
class MilkDecorator implements Coffee {
private Coffee coffee;
public MilkDecorator(Coffee coffee) {
this.coffee = coffee;
}
public double cost() {
return coffee.cost() + 1.0;
}
public String description() {
return coffee.description() + ", Milk";
}
}
라떼(커피 + 밀크)를 만들고 싶으면 이렇게 생성하면 될 것이다.
Coffee latte = new Coffee();
latte = new MilkDecorator(latte);
여기서 MilkDecorator만 빼려면 어떻게 할까?
class NoMilkDecorator implements Coffee {
private Coffee coffee;
public NoMilkDecorator(Coffee coffee) {
this.coffee = coffee;
}
public double cost() {
return coffee.cost() - 1.0; // Counteracts the MilkDecorator
}
public String description() {
return coffee.description().replace(", Milk", ""); // Removes "Milk" from the description
}
}
...
latte = new NoMilkDecorator(latte);
이런 짓을 해야 된다는 거다... 분리는 없다고 생각하고 만드는게 좋을 듯 하다.
'Computer Science > 디자인 패턴' 카테고리의 다른 글
5. 싱글턴 패턴 (Singleton Pattern) (0) | 2023.09.10 |
---|---|
4-2. 추상 팩토리 패턴 (Abstract Factory Pattern) (0) | 2023.09.01 |
4-1. 팩토리 메서드 패턴 (Factory Method Pattern) (0) | 2023.09.01 |
2. 옵저버 패턴 (Observer Pattern) (0) | 2023.08.19 |
1. 스트래티지 패턴 (Strategy Pattern) (0) | 2023.08.11 |
- Total
- Today
- Yesterday
- WORE
- Java #JIT #JVM
- behavior parameterization
- 카카오 인턴
- Java #GC #가비지콜렉터 #Garbage Collector
- 스프링 컨테이너
- 2021
- 2019 Kakao Blind
- 모던 자바 인 액션
- okhttp3
- 스프링
- 코테
- Kakao Blind
- 프로그래밍 모델
- 카카오
- digital tree
- WORA
- nginx 내부
- jvm
- 신규 아이디 추천
- 카카오 코테
- PatternSyntaxException
- 카카오코테
- 디자인패턴
- trie 소스코드
- Java
- 2020 KAKAO
- IOC
- KAKAO 2021
- decorator
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |