참치코더의 꿈 메모장

디자인 패턴 / 데코레이터 패턴 (포장지 패턴) 본문

디자인 패턴

디자인 패턴 / 데코레이터 패턴 (포장지 패턴)

참치깡 2025. 9. 7. 10:54
728x90

- 구조 패턴 중 하나

- 객체에 기능을 상속(Inheritance) 대신 합성(Composition)을 통해 동적으로 확장 하는 방법

- 즉, 원래 코드를 수정하지 않고도 기능을 덧붙일 수 있는 패턴

 

동작 방식

 

1. 기본 컴포넌트(Concrete Component) : 기본 기능을 가진 객체

2. 컴포넌트 인터페이스(Component) : 모든 객체가 따라야 할 공통 인터페이스

3. 데코레이터(Decorator) : 기본 객체를 감싸 주면서 추가 기능을 제공한다

 

- 호출 흐름은 데코레이터 -> 실제 객체로 이어짐

 - 데코레이터는 자신이 감싸고 있는 객체의 메서드를 호출한 뒤 기능을 확장한다.

 

장점

- 기존 코드를 수정하지 않고, 확장이 가능하다. (OCP, 개방-폐쇄 원칙)

- 여러 데코레이터를 조합해 다양한 기능 제공한다

- 상속보다 유연한 구조를 가진다.

 

단점

- 데코레이터가 많아지면 구조가 복잡해진다.

- 디버깅 시 호출 흐름이 꼬일 수 있다.

 

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 도메인/인터페이스 정의
 
// 포인트 적립 서비스 인터페이스
public interface pointService {
    int calculatePoints(int amount); // 금액에 따른 적립 포인트 계산
}
 
 
// 기본 서비스(ConcreteComponent)
 
@Service
public class BasicPointService implements PointService {
    
    @Override
    public int calculatePoints(int amount){
        return amount / 10// 10% 적립
    }
}
 
// 데코레이터 추상 클래스
 
 
// 기존 서비스 객체를 감싸면서 기능을 확장 가능
// calculatePoints 기본 동작은 위임(delegate)
 
public abstract class PointDecorator implements PointService {
 
    protected final PointService decoratedPointService;
 
    public PointDecorator(PointService decoratedPointService){
        this.decoratedPointService = decoratedPointService;
    }
 
    @Override
    public int calculatePoints(int amount){
        return decoratedPointService.calculatePoints(amount);
    }
 
}
 
// 구체 데코레이터 (ConcreteDecorator)
 
// 특별 이벤트 적립률 추가 (+50%)
 
@Component
public class EventPointDecorator extends PointDecorator {
    public EventPointDecorator (PointService decoratedPointService){
        super(decoratedPointService);
    }
 
    @Override
    public int calculatePoints(int amount) {
        int basicPoints = super.calculatePoints(amount);
        int bonusPoints = (int) (basePoints * 0.5); // 이벤트 보너스
        
        return basePoints + bonusPoints;
    }
}
 
// VIP 회원 추가 적립 (+20%)
 
@Component
public class VipPointDecorator extends PointDecorator {
 
    public VipPointDecorator(PointService decoratedPointService) {
        super(decoratedPointService);
    }
 
    @Override
    public int calculatePoints(int amount) {
        int basePoints = super.calculatePoints(amount);
        int vipBonus = (int) (basePoints * 0.2); // VIP 보너스
 
        return basePoints + vipBonus;
    }
}
 
// 서비스 조합 (Spring Configuration)
 
// 런타임에 기능을 동적으로 조합
// 원하는 데코레이터를 추가/제거 가능
 
@Configuration
public class PointConfig{
 
    @Bean
    public PointService pointService(BasicPointService basicPointService){
        return new VipPointDecorator(new EventPointDecorator(basicPointService));
    }
}
 
// 테스트
 
@SpringBootTest
class PointServiceTest {
    
    @Autowired
    private PointService pointService;
 
    @Test
    void testCalculatePoints(){
        int points = pointService.calculatePoints(1000);
        System.out.println("적립 포인트: " + points);
        assertEquals(100 + 50 + 30 , points);
    }
}
 
 
 
 
 
cs

 

 

- 새로운 이벤트나 VIP 정책을 데코레이터로 겹겹히 감싸기만 하면 됨 -> 확장성이 뛰어남

 

 

728x90
Comments