데코레이터 패턴

이종찬·2023년 2월 6일
0
post-custom-banner

📖 데코레이터 패턴?

기존 클래스는 유지하되, 이후 필요한 형태로 꾸밀 때 사용합니다. 확장이 필요한 경우 상속의 대안으로도 활용합니다. SOLID중 개방 폐쇄 원칙과 의존 역전 원칙을 따릅니다.

즉, 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우 각 추가 기능을 데코레이터 클래스로 정의한 이후 조합함으로 추가 기능의 조합을 설계하는 방식입니다.

🤔 사용해야 하는 이유는?

기본 기능에 추가할 수 있는 많은 종류로 다양한 조합을 동적으로 구현할 수 있습니다. 여러 요소들을 조합해서 사용하는 클래스 구조인 경우와 클래스의 요소들을 계속해서 수정하며 사용해야하는 경우에 필요합니다.

❌ 문제가 되는 경우

  • 의미 없는 객체들이 너무 많이 추가되거나 너무 많이 사용하면 코드가 굉장히 복잡해집니다.
  • 특정 구성요소에 대한 예외적인 작업을 할 때 데코레이터 패턴을 사용하기 힘듭니다.

필요이상의 복잡도가 올라갈 수 있으며 그로 인한 초기화 및 가독성이 떨어진다.

😮 사용되는 사례

매번 새로운 객체를 만들고 기능을 추가하는 것이 비효율적일 때 사용합니다. 예를 들어 KIA에 K시리즈 같은 경우 K3,K5,K7이 있습니다. 해당 객체에 대한 정보를 전부 클래스로 나뉘어 구현한다고 하면 상당히 비효율적입니다. 차의 이름과 가격만 표시한다고 가정하면 자동차 회사는 전부 KIA라는 틀을 추상적으로 가지고 차 종류가 변경될 때 이름과 가격만 장식하는 방식을 사용하는 것입니다.

즉, 데코레이터 패턴을 사용하면 기본적 데이터에 추가할 데이터가 다양하고 일정하지 않을 경우 효율적이다.

🤔 Adapter vs Decorator

기존 객체를 변경하지 않고 기능을 추가하는 것은 동일합니다.

어댑터 패턴의 경우 인터페이스를 변경해서 클라이언트에 필요로 한 인터페이스로 적용시킵니다. 즉, 클라이언트에서 요구하는 호환성을 위해 사용됩니다.

데코레이터 패턴의 경우 인터페이스는 바꾸지 않고 기능만 추가하는 것입니다.

👨‍💻 구현

Interface

public interface ICar {
    int getPrice();

    void showPrice();
}

Decorator

public class KIADecorator implements ICar {
    protected ICar kia;
    protected String name;
    protected int price;

    public KIADecorator(ICar kia, String name, int price) {
        this.kia = kia;
        this.name = name;
        this.price = price;
    }

    @Override
    public int getPrice() {
        return kia.getPrice() + price;
    }

    @Override
    public void showPrice() {
        System.out.println(name + "의 가격은 " + getPrice() + "만원 입니다.");
    }
}

KIA, K3, K5, K7

class KIA implements ICar {
    private int price;

    public KIA(int price) {
        this.price = price;
    }

    @Override
    public int getPrice() {
        return price;
    }

    @Override
    public void showPrice() {
        System.out.println("KIA Car price : " + price);
    }
}

class K3 extends KIADecorator {
    public K3(ICar kia, String name) {
        super(kia, name,1000);
    }
}
    
class K5 extends KIADecorator {
    public K5(ICar kia, String name) {
        super(kia, name, 2500);
    }
}

class K7 extends KIADecorator {

    public K7(ICar kia, String name) {
        super(kia, name, 4000);
    }
}

public class Main {
    public static void main(String[] args) {
        ICar kia = new KIA(1000);
        kia.showPrice();

        ICar[] kiaCars = {new K3(kia, "K3"), new K5(kia, "K5"), new K7(kia, "K7")};

        for (ICar car : kiaCars) {
            car.showPrice();
        }
    }
}

실행결과

KIA Car price : 1000
K3의 가격은 2000만원 입니다.
K5의 가격은 3500만원 입니다.
K7의 가격은 5000만원 입니다.

✅ 요약

  • 데코레이터 패턴은 기본 기능을 가지고 있는 클래스에 추가할 수 있는 기능들을 추가하기 편하게 설계하는 방식
  • 너무 많은 사용은 복잡도 증가로 인한 가독성 감소, 초기화 어려움있다. 또한 예외적인 사항을 넣는 것에 어려움도 있다.
  • Adapter패턴은 클라이언트에서 요구하는 인터페이스로 적용시키기위한 호환성의 목적, Decorator패턴은 기존 객체를 자익하는데 사용 목적
profile
왜? 라는 질문이 사라질 때까지
post-custom-banner

0개의 댓글