프로그래밍/디자인패턴

디자인 패턴 - Decorator (Structural Pattern)

aiemag 2021. 2. 17. 12:36
반응형

Structural Pattern인 Decorator Pattern에 대해 정리합니다.

 

이 패턴은 기존의 Object를 동적으로 Responsibility를 추가하거나 수정하여 무한히 장식할 수 있는 패턴입니다. :)

 


Definition

 

Object에 '장식'을 해 나가는 패턴

- alt. Object에 동적으로 responsibility를 추가하기 위해 inheritance 대신 사용할 수 있는 패턴

 

 

목적

- 현재 Object의 responsibility 또는 behavior를 수정하기 위해 동적으로 responsibility를 추가하기 위함.

 

사용 시점

- Object의 responsibility 및 behavior가 동적으로 수정되어야 할 때

- Object를 생성할 때 responsibility와 behavior의 coupling을 낮춰야 할 때

- Object의 수정을 위한 Subclassing이 부적절하거나, 불가능할 때

※ 기능이 필요할 때마다 Subclassing을 한다면 Subclassing 되는 Object의 폭발현상이 일어남.

- Object의 계층에서 구체적인 기능이 배치되어서는 안될 때

- 많고 작은 여러개의 Object들을 둘러싸는 실제 구현이 필요로할 때

 

 

Decorator Pattern은 Structural Pattern에 해당합니다.

 


Class Diagram

Decorator 패턴은 장식을 하는 것입니다.

 

아래 UML에서는 Component class 가 알맹이에 해당하고, Decorator class 가 Component의 instance를 가지며 장식을 합니다.

 

ConcreteComponent는 Component(abstraction class)의 실체 class 이며, ConcreteDecorator는 Decorator(abstraction class)의 실체 class 입니다. 

 


Sample Case

다음은 Decorator 패턴을 적용한 시나리오입니다.

 

Scenario

사랑하는 연인을 위하여 발렌타인데이를 맞이하여 Chocolate 을 만들었습니다.

만들어진 Chocolate을 알맹이만 쏙 줄 수는 없어서 Wrapping 에 감싸서 주기로 하였습니다.

 

Class Diagram

Chocolate class는 초콜렛 자체를 나타내는 abstraction class이며 ConcreteChocolate calss 는 초콜렛을 실체화하는 class입니다.

 

Wrapping class는 chocolate instance를 가지는 Decorator 역할을 하는 abstraction class이며, ConcreteWrapping class는 Wrapping class를 실체화합니다.

 

모든 class는 Chocolate class로부터 상속을 받았으므로, display 메소드를 통해 화면에 출력할 수 있습니다.

 

Code

 

1
2
3
4
5
6
7
8
9
10
11
public class Main {
    public static void main(String[] args) {
        Chocolate c = new ConcreteWrapping(
                          new ConcreteWrapping(
                              new ConcreteChocolate("Dark Chocolate")
                          )                          
                      );
        
        c.display();
    }
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
public abstract class Chocolate {
    public abstract int getWidth();    
    public abstract int getHeight();    
    public abstract String getName(int rowIndex);
    
    public final void display() {
        for(int i=0 ; i<getHeight() ; i++) {
            System.out.println(getName(i));
        }
    }
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteChocolate extends Chocolate{
    private String name;
    
    public ConcreteChocolate(String name) {
        this.name = name;
    }
    
    public int getWidth() {
        return this.name.getBytes().length;
    }
    public int getHeight() {
        return 1;
    }
    
    public String getName(int rowIndex) {
        if(rowIndex==0return name;
        else return null;
    }
}
cs

 

1
2
3
4
5
6
7
public abstract class Wrapping extends Chocolate{
    protected Chocolate chocolate;
    
    protected Wrapping(Chocolate chocolate) {
        this.chocolate = chocolate;
    }
}
cs

 

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
public class ConcreteWrapping extends Wrapping{
    public ConcreteWrapping(Chocolate chocolate) {
        super(chocolate);        
    }
    
    public int getWidth() {
        return 1 + chocolate.getWidth() + 1;
    }
    
    public int getHeight() {
        return 1 + chocolate.getHeight() + 1;
    }
    
    public String getName(int rowIndex) {
        if(rowIndex==0 || rowIndex == chocolate.getHeight() + 1) {
            return "+" + wrap('-', chocolate.getWidth()) + "+";
                    
        }else {
            return "|" + chocolate.getName(rowIndex - 1+ "|";
        }
    }
    
    private String wrap(char ch, int count) {
        StringBuffer buf = new StringBuffer();
        for(int i=0 ; i<count ; i++) {
            buf.append(ch);
        }
        
        return buf.toString();
    }
}
cs

 

실행 결과입니다.

 

Dark Chocolate을 두 겹으로 포장하였습니다. ;)

 

반응형