데코레이터 디자인 패턴

Dec 27 2022
내가 원하는 경우에도 내 자신을 우선으로 할 수 있습니다. 지불이라는 인터페이스에는 지불 방법이 포함되어 있습니다. 그리고 메서드를 구현하기 위한 구체적인 클래스 DefaultPayment는 지불 논리를 수행하기 위해 가격을 인수로 사용합니다.

나 자신을 우선으로 할 수 있어 나중에도

네스카페 컵을 만들기 전에 설탕을 추가하거나 상단에 초콜릿을 추가할 수도 있습니다.

결제 라는 인터페이스에는 결제 방법 이 포함되어 있습니다 .

그리고 메서드를 구현하기 위한 구체적인 클래스 DefaultPayment 는 지불 논리를 수행하기 위해 가격 을 인수로 사용합니다.

public interface Payment {
    void pay(BigDecimal price);
}
public class DefaultPayment implements Payment {
    @Override
    public void pay(BigDecimal price) {
        System.out.println("Payed: " + price);
    }
}

public class Client {
    public static void main(String[] args) {
        Payment defaultPayment = new DefaultPayment();
        defaultPayment.pay(BigDecimal.valueOf(100));
    }
}

내가 지불하고 싶은 제품에 세금이 있다고 가정해 보겠습니다. 어떻게 해야 합니까?

우리는 구현이 아닌 인터페이스를 위해 설계한 설계를 활용할 수 있습니다.

따라서 우리는 Payment 에 대한 클래스를 생성하고 이를 Payment 유형으로 지정 하고 동시에 Payment 를 속성으로 보유할 수 있으며 생성자를 사용하여 지불을 받을 수 있으며 이 클래스를 TaxPaymentDecorator 라고 부를 것 입니다.

public class TaxPaymentDecorator implements Payment {
    private Payment payment;
    private BigDecimal tax = BigDecimal.ZERO;

    public TaxPaymentDecorator(Payment payment) {
        this.payment = payment;
    }

    @Override
    public void pay(BigDecimal price) {
        price = price.add(this.tax);
        payment.pay(price);
    }

    public BigDecimal getTax() {
        return tax;
    }

    public void setTax(BigDecimal tax) {
        this.tax = tax;
    }
}

클라이언트는 다음과 같이 TaxPaymentDecorator 를 사용할 수 있습니다.

public class Client {
    public static void main(String[] args) {
        TaxPaymentDecorator taxPaymentDecorator = new TaxPaymentDecorator(new DefaultPayment());
        taxPaymentDecorator.setTax(BigDecimal.valueOf(50));
        taxPaymentDecorator.pay(BigDecimal.valueOf(100));
    }
}

public class DiscountPaymentDecorator implements Payment {
    private Payment payment;
    private BigDecimal discount = BigDecimal.ZERO;

    public DiscountPaymentDecorator(Payment payment) {
        this.payment = payment;
    }

    @Override
    public void pay(BigDecimal price) {
        price = price.subtract(discount);
        payment.pay(price);
    }

    public BigDecimal getDiscount() {
        return discount;
    }

    public void setDiscount(BigDecimal discount) {
        this.discount = discount;
    }
}

public class Client {
    public static void main(String[] args) {
        TaxPaymentDecorator taxPaymentDecorator = new TaxPaymentDecorator(new DefaultPayment());
        taxPaymentDecorator.setTax(BigDecimal.valueOf(50));
        DiscountPaymentDecorator discountPaymentDecorator = new DiscountPaymentDecorator(taxPaymentDecorator);
        discountPaymentDecorator.setDiscount(BigDecimal.valueOf(10));
        discountPaymentDecorator.pay(BigDecimal.valueOf(100));
    }
}

UML 다이어그램

데코레이터 디자인 패턴 UML 다이어그램

설계 원칙

여기서는 세 가지 디자인 원칙에 대해 설명하겠습니다.

  1. 구현이 아닌 인터페이스를 위한 설계: 보시다시피 지불 인터페이스를 사용하면 이전 구현에 대한 수정 없이 런타임에 동작을 동적으로 추가하여 OOP의 기능을 사용할 수 있었습니다.
  2. 수정을 위해 디자인을 열고 확장 닫기: 이 점은 이전 항목과 관련이 있습니다. 이전 구현에서는 수정을 강제하지 않았지만 이전 구현을 확장하여 새 동작을 추가했습니다. 여기서는 대신 인터페이스를 사용한다는 의미입니다. 구현을 위한 디자인.
  3. 상속보다 구성 선호: 데코레이터는 상속을 사용하는 대신 여기에서 구성을 사용합니다. 일반적으로 상속을 사용하면 결합이 증가하여 문제가 발생할 수 있으며 별도의 블로그에서 이에 대해 논의할 수 있습니다.

개체에 동적으로 추가 책임을 부여합니다. 데코레이터는 기능 확장을 위한 하위 분류에 대한 유연한 대안을 제공합니다.

우리의 예에서는 런타임에 두 가지 책임을 동적으로 연결했습니다. 가격에 세금을 추가하고 가격에서 할인을 뺍니다. DefaultPayment에 대한 하위 분류를 수행하지 않았습니다. OOP 의 기능을 사용하여 유연한 방식으로 동작을 추가했습니다. 이것이 데코레이터 패턴 입니다.