[디자인패턴] 데커레이터패턴(Decorator Pattern)

고지훈·2021년 9월 22일
1

DesignPattern

목록 보기
6/16
post-thumbnail

데커레이터패턴(Decorator Pattern)

데커레이터는 기존의 기능에 새로운 기능을 추가하고 싶을 경우, 새로운 기능을 Decorator로 만들어서 추가하는 방식이다.

예시로, 햄버거를 생각해보자. 우리는 햄버거를 만들 때 제조자의 기호에 따라 야채와 고기를 선택할 수 있다.

즉, 기본 빵 위에 채소와 고기 토핑을 추가하여 햄버거가 완성된다. 여기서 채소와 고기 각각의 재료는 데커레이터가 된다.

데커레이터패턴을 사용하면 기능이 정해진 객체가 아닌, 동적으로 기능을 조합하여 객체를 만드는 것이 가능해진다.

위의 햄버거 만들기를 예로 데커레이터 패턴이 필요한 이유에 대해 알아보자.


왜, 데커레이터패턴을 사용할까?

햄버거를 만들기 위해서는 기본적으로 빵이 필요하다.
빵 위에 함께 곁들여 먹을 토핑이 필요하고, 여기서는 양상추, 피클, 패티로 구성하겠다.

위 재료로 만들 수 있는 햄버거는 다음과 같다.
1) 빵
2) 양상추가 있는 빵
3) 피클이 있는 빵
4) 패티가 있는 빵

// Hamburger.java
public class Hamburger {
    public void make() {
        System.out.println("빵 추가");
    }
}
// HamburgerWithLettuce.java
public class HamburgerWithLettuce extends Hamburger {
    public void make() {
        super.make();
        addLettuce();
    }

    private void addLettuce() {
        System.out.println("양상추 추가");
    }
}
// HamburgerWithPickle.java
public class HamburgerWithPickle extends Hamburger {
    public void make() {
    	super.make();
        addPickle();
    }
    
    private void addPickle() {
    	System.out.println("피클 추가");
    }
}
// HamburgerWithPatty.java
public class HamburgerWithPatty extends Hamburger {
    public void make() {
        super.make();
        addPatty();
    }

    private void addPatty() {
        System.out.println("패티 추가");
    }
}
// Client.java
public class Client {
    public static void main(String args[]) {
        Hamburger hamburger = new Hamburger();
        hamburger.make();

        HamburgerWithLettuce hamburgerWithLettuce = new HamburgerWithLettuce();
        hamburgerWithLettuce.make();

        HamburgerWithPickle hamburgerWithPickle = new HamburgerWithPickle();
        hamburgerWithPickle.make();

        HamburgerWithPatty hamburgerWithPatty = new HamburgerWithPatty();
        hamburgerWithPatty.make();
    }
}

위 코드의 결과는 하나의 빵에 하나의 재료만 속해있는 상태이다. 만약 양상추, 피클, 패티가 모두 들어있는 햄버거를 만드려면 어떻게 해야할까?

HamburgerWithLettuceAndPickleAndPatty 클래스를 만들어야 할 것이다.

// HamburgerWithLettuceAndPickleAndPatty.java
public class HamburgerWithLettuceAndPickleAndPatty extends Hamburger {
    public void make() {
        super.make();
        addLettuce();
        addPickle();
        addPatty();
    }

    private void addLettuce() {
        System.out.println("양상추 추가");
    }

    private void addPickle() {
        System.out.println("피클 추가");
    }

    private void addPatty() {
        System.out.println("패티 추가");
    }
}

현재는 3개의 토핑재료만 속한 상태이지만, 향후 여러개의 토핑이 생길 경우 토핑이 추가될 때 마다 새로운 클래스를 생성해주어야 한다.

위와 같은 방법을 효율적으로 해결하기 위해 데커레이터패턴을 사용해보자!


데커레이터패턴 구현

Hamburger 추상클래스를 정의한다.
양상추 햄버거, 피클 햄버거, 패티 햄버거 등 여러종류의 햄버거를 만드는 것을 캡슐화하기 위해서이다.

// Hamburger.java
public abstract class Hamburger {
    public abstract void make();
}

토핑을 추가하는 ToppingDecorator클래스를 정의한다.
ToppingDecorator클래스는 햄버거를 토핑하는 것으로, Hamburger클래스를 상속받는다.

// ToppingDecorator.java
public class ToppingDecorator extends Hamburger {
	private Hamburger hamburger;
    
    public ToppingDecorator(Hamburger hamburger) {
    	this.hamburger = hamburger;
    }
    
    public void make(){
    	hamburger.make();
    }
}

햄버거의 재료 중 하나인 빵을 추가하기 위한 Bread클래스를 정의한다.
빵은 토핑이 아닌, 기본적으로 있어야하는 재료이기 때문에 데커레이터로 정의하지 않는다.

// Bread.java
public class Bread extends Hamburger {
    public void make() {
    	System.out.println("빵 추가");
    }
}

양상추, 피클, 패티와 같은 토핑을 추가하기 위한 LettuceDecorator, PickleDecorator, PattyDecorator클래스를 정의한다.

// LettuceDecorator.java
public class LettuceDecorator extends ToppingDecorator {
    public LettuceDecorator(Hamburger hamburger) {
        super(hamburger);
    }

    public void make() {
        super.make();
        addLettuce();
    }

    private void addLettuce() {
        System.out.println("양상추 추가");
    }
}
// PickleDecorator.java
public class PickleDecorator extends ToppingDecorator {
	public PickleDecorator(Hamburger hamburger) {
    	super(hamburger);
    }
    
    public void make() {
    	super.make();
        addPickle();
    }
    
    private void addPickle() {
    	System.out.println("피클 추가");
    }
}
// PattyDecorator.java
public class PattyDecorator extends ToppingDecorator {
	public PattyDecorator(Hamburger hamburger) {
    	super(hamburger);
    }
    
    public void make() {
    	super.make();
        addPatty();
    }
    
    private void addPatty() {
    	System.out.println("패티 추가");
    }
}

마지막으로 햄버거를 만드는 Client클래스를 정의한다.

// Client.java
public class Client {
    public static void main(String args[]) {
        // 양상추 햄버거
        Hamburger hamburgerWithLettuce = new LettuceDecorator(new Bread());
        hamburgerWithLettuce.make();

        // 패티, 피클 햄버거
        Hamburger hamburgerWithPickleAndPatty = new PattyDecorator(new PickleDecorator(new Bread()));
        hamburgerWithPickleAndPatty.make();
    }
}

데커레이터 객체를 생성할 때, 생성자로 다시 데커레이터를 생성하고, 최종적으로 Bread객체를 생성한다.

위의 패턴을 적용할 경우 토핑이 더 늘어나도 이와 같이 데커레이터 객체를 생성함으로써 원하는 토핑의 햄버거를 만들 수 있다.

데커레이터 패턴 학습 시 참고 블로그: https://victorydntmd.tistory.com/297?category=719467

profile
"계획에 따르기보다 변화에 대응하기를"

0개의 댓글