Design Pattern | Decorator Pattern (데코레이터 패턴)

Decorator 패턴이란?

  • Decorator라는 영어 단어는 장식하다(Decorate)라는 의미이다. 기존 내용에 무엇인가를 덧붙인다는 의미가 강하다.
  • Decorator 패턴은, 객체에 계속해서 데코레이션(장식)을 추가하는 방식이다.
  • 피자를 예를 들면, 기존 피자 위에 토핑을 추가한다고 생각하면 된다.
  • 스폰지 케이크에 대해 크림, 초콜릿, 딸기 … 등으로 장식 할 수 있도록 객체도 기능을 하나 하나 씌워 장식하는 방식이 된다.
  • GoF 디자인 패턴은 생성과 관련된 디자인 패턴으로 분류된다.

Decorator 패턴 예제 프로그램

입력한 문자열에 대해 테두리 등의 장식을 하는 예제 프로그램이다.

Class Diagram
Decorator Pattern Class Diagram

1. Display 클래스

캐릭터 라인 표시용의 추상 클래스이다.

Display.java

package com.devkuma.designpattern.structural.decorator;

public abstract class Display {

    // 열의 문자 수를 반환한다.
    public abstract int getColumns();

    // 행 수를 반환한다.
    public abstract int getRows();

    // 지정된 행의 문자열을 반환환다.
    public abstract String getRowText(int row);

    public void show() {
        for (int i = 0; i < getRows(); i++) {
            System.out.println(getRowText(i));
        }
    }
}

2. StringDisplay 클래스

1행만으로 이루어지는 캐릭터 라인 표시용의 클래스이다.

StringDisplay.java

package com.devkuma.designpattern.structural.decorator;

public class StringDisplay extends Display {

    private String string;

    public StringDisplay(String string) {
        this.string = string;
    }

    public int getColumns() {

        return string.getBytes().length;
    }

    public int getRows() {
        return 1;
    }

    public String getRowText(int row) {
        return (row == 0) ? string : null;
    }
}

3. Border 클래스

장식 테두리를 나타내는 추상 클래스이다.

Border.java

package com.devkuma.designpattern.structural.decorator;

public abstract class Border extends Display {

    protected Display display;

    protected Border(Display display) {
        this.display = display;
    }
}

4. SideBorder 클래스

좌우에 장식 테두리를 붙이는 클래스이다.

SideBorder.java

package com.devkuma.designpattern.structural.decorator;

public class SideBorder extends Border {

    public SideBorder(Display display) {
        super(display);
    }

    public int getColumns() {
        // 문자수는 내용의 양측에 장식한 문자 수를 더한 수
        return 1 + display.getColumns() + 1;
    }

    public int getRows() {
        // 행 수는 내용의 행 수와 동일
        return display.getRows();
    }

    public String getRowText(int row) {
        return "*" + display.getRowText(row) + "*";
    }
}

5. FullBorder 클래스

상하 좌우에 장식 테두리를 붙이는 클래스이다.

FullBorder.java

package com.devkuma.designpattern.structural.decorator;

public class FullBorder extends Border {

    public FullBorder(Display display) {
        super(display);
    }

    public int getColumns() {
        // 문자수는 내용의 양측에 좌우의 장식한 문자 수를 더한 수
        return 1 + display.getColumns() + 1;
    }

    public int getRows() {
        // 행수는 내용의 행수에 상하의 장식한 문자 수을 더한 수
        return 1 + display.getRows() + 1;
    }

    public String getRowText(int row) {
        if (row == 0) {
            // 상단 테두리
            return "+" + makeLine('-', display.getColumns()) + "+";
        } else if (row == display.getRows() + 1) {
            // 하단 테두리
            return "+" + makeLine('-', display.getColumns()) + "+";
        } else {
            // 그밖에
            return "|" + display.getRowText(row - 1) + "|";
        }
    }

    private String makeLine(char ch, int count) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < count; i++) {
            buf.append(ch);
        }
        return buf.toString();
    }
}

6. Main 클래스

메인 처리를 실행하는 클래스이다.

Main.java

package com.devkuma.designpattern.structural.decorator;

public class Main {

    public static void main(String[] args) {
        Display display1 = new StringDisplay("Hello world");
        display1.show();
        System.out.println("");

        Display display2 = new SideBorder(display1);
        display2.show();
        System.out.println("");

        Display display3 = new FullBorder(display2);
        display3.show();
        System.out.println("");

        Display display4 =
                new FullBorder(
                        new SideBorder(
                                new FullBorder(
                                        new StringDisplay("Hello world"))));
        display4.show();
    }
}

7. 실행 결과

Hello world

*Hello world*

+-------------+
|*Hello world*|
+-------------+

+---------------+
|*+-----------+*|
|*|Hello world|*|
|*+-----------+*|
+---------------+

Decorator 패턴 장점

Decorator 패턴에서는, 장식 테두리(Border)도 내용(StringDisplay)도 공통의 인터페이스를 가지고 있다. 인터페이스는 공통이지만, 감싸면 감을수록 기능이 추가되어 간다. 그 때, 감싸는 쪽을 수정할 필요는 없다. 감싸는 것을 변경하지 않고 기능을 추가 할 수 있다.