Статьи

Структурные Проектные Образцы: Образец Декоратора

Ранее мы изменили поведение наших абстрактных объектов, используя шаблон моста , и реализовали древовидную структуру для наших компонентов, используя составной шаблон и делегируя запросы.

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

Итак, представьте сценарий применения различных скидок к одному из наших продуктов.

Мы начнем с интерфейса скидок, чтобы указать действие скидки.

1
2
3
4
5
6
7
8
9
package com.gkatzioura.design.structural.decorator;
 
import java.math.BigDecimal;
 
public interface Discount {
     
    BigDecimal apply(BigDecimal originalPrice);
     
}

Объект, который реализует скидку, применяет скидку и возвращает скидку.
Одна из скидок, которую мы хотим применить, — это специальная скидка для вновь зарегистрированных пользователей.
У новых зарегистрированных участников будет скидка 20% на первый заказ

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package com.gkatzioura.design.structural.decorator;
 
import java.math.BigDecimal;
 
public class NewlyRegisteredDiscount implements Discount {
 
    public static final BigDecimal SEVENTY_FIVE = new BigDecimal(75);
    public static final BigDecimal ONE_HUNDRED = new BigDecimal(100);
 
    @Override
    public BigDecimal apply(BigDecimal originalPrice) {
 
        return originalPrice.multiply(SEVENTY_FIVE).divide(ONE_HUNDRED);
    }
 
}

Как вы уже испытывали, иногда применяется более одной скидки.
Наша система также предоставляет нашим клиентам купонные скидки от 5 долларов.

Очень удобно применять несколько скидок, и наши системы должны поддерживать это действие. Обычно создаются пакеты скидок, которые на самом деле являются просто набором пакетов.
Для этого мы будем использовать шаблон декоратора.
Мы создадим декоратор скидок.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.gkatzioura.design.structural.decorator;
 
import java.math.BigDecimal;
 
public class DiscountDecorator implements Discount {
 
    protected Discount discount;
 
    public DiscountDecorator(Discount discount) {
        this.discount = discount;
    }
 
    @Override
    public BigDecimal apply(BigDecimal originalPrice) {
        return discount.apply(originalPrice);
    }
}

Поэтому мы хотим получить скидку при первой покупке и скидку в 5 долларов для новых пользователей, на которые ссылаются другие пользователи.
Мы будем называть эту скидку скидкой для «указанного пользователя».
Цель состоит в том, чтобы получить 5 долларов и применить скидку к первоначальной цене.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.gkatzioura.design.structural.decorator;
 
import java.math.BigDecimal;
 
public class ReferencedUserDiscount extends DiscountDecorator {
 
    public static final BigDecimal FIVE = new BigDecimal(5);
 
    public ReferencedUserDiscount(Discount discount) {
        super(discount);
    }
     
    @Override
    public BigDecimal apply(BigDecimal originalPrice) {
 
        BigDecimal discountedPrice = super.apply(originalPrice);
 
        if(discountedPrice.compareTo(FIVE)<=0) {
            return discountedPrice;
        }
 
        return discountedPrice.subtract(FIVE);
    }
}

Представьте себе сейчас сценарий скидки лояльности. Мы хотим двухлетний план для наших пользователей со скидкой 5% на каждый их заказ.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package com.gkatzioura.design.structural.decorator;
 
import java.math.BigDecimal;
 
public class TwoYearPlanDiscount extends DiscountDecorator {
 
    public static final BigDecimal NINETY_NINE = new BigDecimal(95);
    public static final BigDecimal ONE_HUNDRED = new BigDecimal(100);
 
    public TwoYearPlanDiscount(Discount discount) {
        super(discount);
    }
 
    @Override
    public BigDecimal apply(BigDecimal originalPrice) {
        return super.apply(originalPrice).multiply(NINETY_NINE).divide(ONE_HUNDRED);
    }
 
}

Как вы можете видеть как в случае скидок для указанных пользователей, так и в случае двухлетней скидки, мы не изменили нашу первоначальную скидку для наших новых зарегистрированных пользователей. То, что мы сделали, это украсили оригинальную скидку и вернули скидку, учитывая различные варианты.

Шаблон декоратора

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

Итак, давайте воплотим наш план в жизнь.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package com.gkatzioura.design.structural.decorator;
 
import java.math.BigDecimal;
 
public class DecoratorScenario {
 
    public static void main(String args[]) {
 
        NewlyRegisteredDiscount newlyRegisteredDiscount = new NewlyRegisteredDiscount();
        ReferencedUserDiscount referencedUserDiscount = new ReferencedUserDiscount(newlyRegisteredDiscount);
        TwoYearPlanDiscount twoYearPlanDiscount = new TwoYearPlanDiscount(referencedUserDiscount);
        BigDecimal discountPrice = twoYearPlanDiscount.apply(new BigDecimal(100));
    }
 
}

Наши новые зарегистрированные пользователи будут иметь три вида скидок.

Вы можете найти исходный код на github .

Опубликовано на Java Code Geeks с разрешения Эммануила Гкациоураса, партнера нашей программы JCG. См. Оригинальную статью здесь: Структурные Проектные Образцы: Образец Декоратора

Мнения, высказанные участниками Java Code Geeks, являются их собственными.