Шаблон оформления декоратора

Dec 27 2022
Я могу поставить себя на первое место, даже если захочу. У нас есть интерфейс под названием «Оплата», который содержит метод оплаты. И конкретный класс DefaultPayment для реализации метода, метод принимает цену в качестве аргумента для выполнения логики оплаты.

Я могу поставить себя на первое место, даже после, если захочу

Вы можете добавить сахар перед приготовлением Nescafe или даже добавить сверху шоколад.

У нас есть интерфейс под названием Payment , который содержит метод оплаты .

И конкретный класс 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));
    }
}

Допустим, есть налог на продукт, за который я хочу заплатить, что я могу сделать?

Мы можем воспользоваться нашим дизайном, мы разработали интерфейс, а не реализацию.

Итак, мы можем создать класс для Платежа и пусть он будет типом Платежа и в то же время хранить Платеж как свойство, и он мог бы принимать платеж с помощью своего конструктора, и мы назовем этот класс 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. Дизайн для интерфейса, а не для реализации: как видите, использование интерфейса Payment помогло нам использовать мощь ООП, динамически добавляя поведение во время выполнения без каких-либо изменений для старой реализации.
  2. Сделайте свой дизайн открытым для расширения и закрытия для модификации: этот пункт связан с предыдущим, наша старая реализация не заставляла нас вносить какие-либо изменения, но мы расширили старую реализацию, чтобы добавить новые поведения, и я имею в виду здесь использование вместо этого интерфейса дизайна для реализации.
  3. Предпочитайте композицию наследованию: здесь декоратор использует композицию вместо того, чтобы пытаться использовать наследование, в общем случае использование наследования может вызвать проблемы из-за увеличения связи, и мы могли бы обсудить это в отдельном блоге.

Динамически прикрепляйте дополнительные обязанности к объекту. Декораторы предоставляют гибкую альтернативу подклассам для расширения функциональности.

В нашем примере мы динамически привязываем две обязанности во время выполнения, мы добавляем налог к ​​цене и вычитаем скидку из цены, мы не делали подклассы для DefaultPayment, мы использовали мощь ООП , чтобы гибко добавлять наши поведения, и это шаблон декоратора .