디자인 패턴

자바(JAVA) - 데코레이터(Decorator) 패턴

Hyeongjun_Ham 2023. 10. 19. 18:59

1. 의도

Decorator의 의미인 '장식자, 장식하는 사람'처럼 객체에 동적으로 장식(책임/기능)을 추가 할 수 있게 해주는 구조적인 패턴.

 

2. 용도

객체에 필요한 추가 장식(책임/기능)의 조합을 동적(런타임)으로 할 수 있는 구조다.

즉, 런타임에 동적으로 특정 객체에 장식(책임/기능)들을 조합하여 더 멋지게 만들 수 있도록 해준다.

 

예를 들어, 커피라는 객체를 만들 때 구체화된 컴포넌트는 커피가 되며 물, 우유, 크림, 시럽 등 다양한 장식의 조합이 가능한 경우를 사용할 수 있다.

 

3. UML

 

4. 구현

public class Client {
    public static void main(String[] args) {

        //안에서 밖으로 감싸진다.
        IDrink test = new Coffee();
        IDrink testDrink = new IceDeco(new MilkDeco(test));
        testDrink.desc();
        testDrink.cost();


        //Builder 구현 시
        System.out.println("# Ice Latte");
        DrinkBuilder drinkBuilder = new DrinkBuilder();
        IDrink iceLatte = drinkBuilder.addCoffee().addMilk().addIce().build();
        iceLatte.desc();
        System.out.println(" = Cost: " + iceLatte.cost());

        System.out.println("\n# Strawberry Shake");
        IDrink strawberryShake = drinkBuilder.addShake()
        					.addStrawberry().addMilk().addIce().addSugar().build();
        strawberryShake.desc();
        System.out.println(" = Cost: " + strawberryShake.cost());

    }
}
public interface IDrink {
    void desc();
    int cost();
}
public class Coffee implements IDrink {
    @Override
    public void desc() {
        System.out.println("Coffee");
    }

    @Override
    public int cost() {
        return 2000;
    }
}
public class Shake implements IDrink {
    @Override
    public void desc() {
        System.out.println("Shake " + cost());
    }

    @Override
    public int cost() {
        return 3000;
    }
}
public abstract class DrinkDecorator implements IDrink {

    private IDrink drink;

    public DrinkDecorator(IDrink drink) {
        this.drink = drink;
    }

    @Override
    public void desc() {
        drink.desc();
    }

    @Override
    public int cost() {
        return drink.cost();
    }
}
public class IceDeco extends DrinkDecorator {


    public IceDeco(IDrink drink) {
        super(drink);
    }

    @Override
    public void desc() {
        super.desc();
        System.out.println(" IceDeco " + cost());
    }

    @Override
    public int cost() {
        return super.cost() + 200;
    }
}
public class MilkDeco extends DrinkDecorator {
    public MilkDeco(IDrink drink) {
        super(drink);
    }

    @Override
    public void desc() {
        super.desc();
        System.out.println(" MilkDeco " + cost());
    }

    @Override
    public int cost() {
        return super.cost() + 300;
    }
}
public class StrawberryDeco extends DrinkDecorator {
    public StrawberryDeco(IDrink drink) {
        super(drink);
    }

    @Override
    public void desc() {
        super.desc();
        System.out.println(" StrawberryDeco " + cost());
    }

    @Override
    public int cost() {
        return super.cost() + 500;
    }
}
public class SugarDeco extends DrinkDecorator {
    public SugarDeco(IDrink drink) {
        super(drink);
    }

    @Override
    public void desc() {
        super.desc();
        System.out.println(" SugarDeco " + cost());
    }

    @Override
    public int cost() {
        return super.cost() + 100;
    }
}
public class DrinkBuilder {

    private IDrink drink;

    DrinkBuilder addCoffee() {
        drink = new Coffee();
        return this;
    }

    public DrinkBuilder addShake() {
        drink = new Shake();
        return this;
    }

    public DrinkBuilder addIce() {
        drink = new IceDeco(drink);
        return this;
    }

    public DrinkBuilder addSugar() {
        drink = new SugarDeco(drink);
        return this;
    }

    public DrinkBuilder addMilk() {
        drink = new MilkDeco(drink);
        return this;
    }

    public DrinkBuilder addStrawberry() {
        drink = new StrawberryDeco(drink);
        return this;
    }

    public IDrink build() {
        return drink;
    }
}

 

 

5. 패턴의 장단점

  • 장점
    • 객체를 여러 데코레이터로 조합 할 수 있다.
    • 런타임시 동적으로 조합할 수 있다.
    • 데코레이터 마다 고유의 책임을 갖도록 하여 '단일 책임 원칙(SRP)'을 준수한다.
    • 기존 코드의 수정 없이 데코레이터의 추가로 '개방 폐쇄 원칙(OCP)'를 준수한다.
    • 구현체가 아닌 인터페이스를 바라봄으로써 '의존 역전 원칙(DIP)'를 준수한다.
  • 단점
    • 데코레이터의 일부를 제거하는 건 어려울 수 있다.
    • 데코레이터의 조합 순서에 의존이 생길 수 있다.

 

출처 :

https://refactoring.guru/design-patterns/decorator

 

Decorator

/ Design Patterns / Structural Patterns Decorator Also known as: Wrapper Intent Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors. Prob

refactoring.guru