개발/디자인패턴

[Java][디자인 패턴] 10. 장식자 패턴 (Decorator Pattern)

nova_dev 2022. 3. 9. 23:59
반응형

디자인패턴
[Java][디자인 패턴] 10. 장식자 패턴 (Decorator Pattern)

장식자 패턴은 객체의 동적 기능을 추가하기 위해 구조를 개선하는 패턴이다.
다양한 확장을 위해 객체를 조합한다.

 

 장식자 패턴이란?

  • 장식자 패턴은 동적으로 객체를 결합하기 위해 객체지향의 구성을 통해 확장한다. (코드를 변경하지 않고 확장 가능)
  • 실시간 동작으로 자신의 객체를 확장하면서, 필요로 하는 다양한 책임을 수행하고 문제를 해결해 나간다.
  • 장식자의 기본 배경이 되는 개념은 복합 객체와 위임이다. 상속을 배제하고 구성을 통해 객체를 동적으로 확장한다.

 

 

 장식자 패턴은 언제 사용할까?

넷플릭스 요금제 별 기능

만약 뼈대는 동일하지만 특정 기능들을 확장하거나 삭제하거나 일부만 변경하는 등 객체를 다양하게 확장하려면 어떻게 해야 할까?
이럴 경우 새로운 기능을 추가할 때 객체를 동적으로 결합해서 구성을 통해 객체를 확장할 수 있다.
장식자 패턴은 기존의 객체를 감싸서 새로운 기능을 추가하는 객체를 생성할 때 매우 유용한 패턴이다.

 장식자 패턴 구성 요소

케이크 장식하기

  •  Component (CakeComponent)
    • 인터페이스를 정의한다
  • ConcreateComponent (CakeConcreteComponent)
    • 인터페이스에 정의 실제를 구현한다
  • Decorator (CakeComponentDecorator)
    • 컴포넌트를 참조하며 인터페이스를 일치화한다
  • ConcreateDecorator (Strawberry, Chocolate, Mint)
    • 확장 및 추가되는 기능을 작성한다.

 팩토리 패턴 코드

1. CakeComponent 인터페이스

public interface CakeComponent {
    String decorate();
}

2. CakeConcreteComponent 클래스
케이크의 뼈대 역할을 담당함

public class CakeConcreteComponent implements CakeComponent {
    @Override
    public String decorate() {
        return "케이크";
    }
}

3. CakeComponentDecorator
케이크를 꾸며주는 장식자들의 추상 클래스

public abstract class CakeComponentDecorator implements CakeComponent {
    private final CakeComponent cakeComponent;

    protected CakeComponentDecorator(CakeComponent cakeComponent) {
        this.cakeComponent = cakeComponent;
    }

    @Override
    public String decorate() {
        return cakeComponent.decorate();
    }
}

4. Cake ConcreteDecorator (Strawberry, Chocolate, Mint) 
케이크를 꾸며주는 장식자의 구체 클래스

public class Chocolate extends CakeComponentDecorator {
    public Chocolate(CakeComponent cakeComponent) {
        super(cakeComponent);
    }

    @Override
    public String decorate() {
        return decorateCake() + super.decorate();
    }

    private String decorateCake() {
        return "초코 ";
    }

}
public class Mint extends CakeComponentDecorator {
    public Mint(CakeComponent cakeComponent) {
        super(cakeComponent);
    }

    @Override
    public String decorate() {
        return decorateCake() + super.decorate();
    }

    private String decorateCake() {
        return "민트 ";
    }
    
}
public class Strawberry extends CakeComponentDecorator {
    public Strawberry(CakeComponent cakeComponent) {
        super(cakeComponent);
    }

    @Override
    public String decorate() {
        return decorateCake() + super.decorate();
    }

    private String decorateCake() {
        return "딸기 ";
    }

}

5. 케이크 장식 테스트 코드

class CakeComponentTest {
    @Test
    @DisplayName("초코 케이크")
    void cakeTest1() {
        CakeComponent cakeComponent = new Chocolate(new CakeConcreteComponent());
        assertEquals("초코 케이크", cakeComponent.decorate());
    }

    @Test
    @DisplayName("초코 초코 딸기 케이크")
    void cakeTest2() {
        CakeComponent cakeComponent = new Chocolate(new Chocolate(new Strawberry(new CakeConcreteComponent())));
        assertEquals("초코 초코 딸기 케이크", cakeComponent.decorate());
    }

    @Test
    @DisplayName("민트 딸기 초코 케이크")
    void cakeTest3() {
        CakeComponent cakeComponent = new Mint(new Strawberry(new Chocolate(new CakeConcreteComponent())));
        assertEquals("민트 딸기 초코 케이크", cakeComponent.decorate());
    }
}

 장식자 패턴의 장점과 단점

장점

  • 상속 형태의 확장보다 더 융통성 있게 설계 가능하다.
  • 객체 실행 중에도 동적으로 기능을 추가할 수 있어서 새로운 부가 기능을 추가하는 가장 효과적인 방법이다.
  • 미리 클래스 등의 자원을 생성해 낭비하는 게 아니라 동적으로 처리되는 시점에 자원을 할당받아 사용 가능하다.

 

 

단점

  • 작은 단위의 객체가 많이 생성된다. (단, 작은 코드로 이루어진 객체는 보다 이해하기 쉬우므로 상호 작용을 통해 다른 객체를 생성할 수 있는 원소 객체라 무조건 단점이라고 할 수 없음.)
  • 유사한 성직의 작은 클래스가 증가한다. (완전 다른 클래스는 아니고 상호 작용 방법에만 차이가 있는 클래스들)
  • 기존 객체를 감싸는 과정이 있어야 하므로 구성 요소를 초기화하는 작업이 필요하다.

 

 

 Github 코드

Github 예제 코드 링크

Github 예제 테스트 코드 링크

참고 자료

 

 

 

 

 

반응형