Look at Java Design Patterns from Monkey King’s 72 Changes: Decorator Pattern

Application scenarios

JD.com, Tmall Double Eleven, Valentine's Day merchandise sales, various merchandise have different promotions

  • Full reduction: Full 200 minus 50
  • Every full deduction: every full 100 minus 10
  • Discount: 20% off for two items, 30% off for three items
  • Quantity minus: full three pieces minus the lowest price one

Assuming that a customer buys two pieces of clothes, first discounts it by 20%, and then discounts by 50% for 200, and the promotional activities are multiplied. How can we flexibly calculate the order amount?

Code example

PromotionAlgorithm interface to calculate the order amount:

public interface PromotionAlgorithm {
    
    
    Order promotionAlgorithm(Order order);
}

Implementation class PromotionAlgorithm1, PromotionAlgorithm2:

public class PromotionAlgorithm1 implements PromotionAlgorithm {
    
    
    @Override
    public Order promotionAlgorithm(Order order) {
    
    
        System.out.println("打8折");
        order.setPrice(order.getPrice() * 0.8);
        return order;
    }
}
public class PromotionAlgorithm2 implements PromotionAlgorithm {
    
    
    @Override
    public Order promotionAlgorithm(Order order) {
    
    
        System.out.println("满200减了50");
        order.setPrice(order.getPrice() - 50);
        return order;
    }
}

OrderService class:

public class OrderService {
    
    
    public Order getOrderPrices(Order order, String... promotion) {
    
    
        for (String s : promotion) {
    
    
            if (s.equals("promotion-1")) {
    
    
                new PromotionAlgorithm1().promotionAlgorithm(order);
            } else if (s.equals("promotion-2")) {
    
    
                new PromotionAlgorithm2().promotionAlgorithm(order);
            }
        }
        return order;
    }
}

Test category:

public class Test {
    
    

    public static void main(String[] args) {
    
    
        OrderService service = new OrderService();
        Order order = new Order();
        order.setPrice(1000.0);
        String[] array = {
    
    "promotion-1", "promotion-2"};
        Order result = service.getOrderPrices(order, array);
        System.out.println("最终我花了:" + result.getPrice());
    }
}

Insert picture description here
Now think about it, when multiple methods of a class need to be enhanced, when the user will use the enhanced method at will, is the above for loop still flexible?

Improve the code

Define an interface OrderComponent:

public interface OrderComponent {
    
    

    /** 促销方式 */
    String getPromotion();

    /** 价格 */
    Double getPrice();
}

OrderConcreteComponent class: the class that needs to be calculated

public class OrderConcreteComponent implements OrderComponent {
    
    

    @Override
    public String getPromotion() {
    
    
        return "我买了三件衣服,衣服总共1000元,";
    }

    @Override
    public Double getPrice() {
    
    
        return 1000.0;
    }
}

OrderDecorator class: There is an attribute, which is the above interface OrderComponent, which implements the OrderComponent interface

public class OrderDecorator implements OrderComponent {
    
    

    public OrderComponent component;

    public OrderDecorator(OrderComponent component) {
    
    
        this.component = component;
    }


    @Override
    public String getPromotion() {
    
    
        return this.component.getPromotion();
    }

    @Override
    public Double getPrice() {
    
    
        return this.component.getPrice();
    }
}

OrderDecoratorA class: discount class

public class OrderDecoratorA extends OrderDecorator {
    
    

    public OrderDecoratorA(OrderComponent component) {
    
    
        super(component);
    }

    @Override
    public String getPromotion() {
    
    
        return this.component.getPromotion() + "衣服打了8折,";
    }

    @Override
    public Double getPrice() {
    
    
        return this.component.getPrice() * 0.8;
    }
}

OrderDecoratorB class: full reduction class

public class OrderDecoratorB extends OrderDecorator {
    
    

    public OrderDecoratorB(OrderComponent component) {
    
    
        super(component);
    }

    @Override
    public String getPromotion() {
    
    
        return this.component.getPromotion() + "又满200减了50。";
    }

    @Override
    public Double getPrice() {
    
    
        return this.component.getPrice() - 50;
    }
}

Test category:

public class Test {
    
    

    public static void main(String[] args) {
    
    
        OrderComponent component = new OrderConcreteComponent();
        OrderComponent c = new OrderDecorator(component);
        OrderComponent d = new OrderDecoratorA(c);
        OrderComponent e = new OrderDecoratorB(d);
        System.out.println(e.getPromotion());
        System.out.println("最终我花了:" + e.getPrice());
    }
}

Insert picture description here
The improved code above is the basic use of the decorator pattern

Decorator pattern

definition

In a decorative way, dynamically attach responsibilities to the object without changing its structure

intention

Add some additional responsibilities to an object dynamically. In terms of adding functions, the decorator mode is more flexible than generating subclasses

Mainly solve the problem

In general, we often use inheritance to extend a class, because inheritance introduces static characteristics to the class, and with the increase of extended functions, the subclass will be very swollen.

When to use

Extend the class without adding many subclasses

Pros and cons

advantage:

  1. Do not change the specific class code, dynamically superimpose the enhanced behavior function
  2. To extend functionality, decorators provide a more flexible alternative than inheritance
  3. Decoration class and decorated class can be developed independently and will not be coupled with each other

Disadvantages:

  1. Multi-layer decoration is more complicated

Class diagram:
Insert picture description here
the roles involved:

  1. Abstract construction (Component) role: give an abstract interface to standardize the object that is ready to receive additional responsibilities
  2. Concrete component (ConcreteComponent) role: Define a class that will receive additional responsibilities, that is, the decorated person
  3. Decorator role: Holds an instance of a Component object and defines an interface consistent with the abstract interface
  4. Concrete decoration (ConcreteDecorator) role: that is, DecoratorA and DecoratorB in the above figure, responsible for attaching additional responsibilities to the built object, that is, the specific function enhancement behavior is in this role

Component class:

public interface Component {
    
    
    void sampleOperation();
}

ConcreteComponent class:

public class ConcreteComponent implements Component {
    
    
    @Override
    public void sampleOperation() {
    
    
        //do something
    }
}

Decorator class:

public class Decorator implements Component {
    
    

    private Component component;

    public Decorator(Component component) {
    
    
        this.component = component;
    }

    @Override
    public void sampleOperation() {
    
    
        this.component.sampleOperation();
    }
}

ConcreteDecorator class:

public class ConcreteDecorator extends Decorator {
    
    

    public ConcreteDecorator(Component component) {
    
    
        super(component);
    }

    @Override
    public void sampleOperation() {
    
    
        super.sampleOperation();
    }
}

The decorator pattern is often called the wrapping pattern, because every concrete decoration class wraps the next concrete decoration class or concrete construction class.
Suppose there are two concrete decoration classes Decorator1, Decorator2, and a concrete construction class ConcreteComponent :
Insert picture description here
Yes, that’s right, just like a matryoshka, wrapped in one layer

The Seventy-two Changes of Monkey King

Everyone knows that Monkey King has seventy-two changes, and each change will bring him an additional ability, becoming a fish that can swim in the water, and a bird that can fly in the air, but no matter what the change, Erlang in the eyes, or a monkey
Insert picture description here
monkey categories: like changes have seventy-two

public interface MonkeyKing {
    
    
    /** 七十二变 */
    String change();
}

Great saint deity class:

public class ConcreteMonkey implements MonkeyKing {
    
    
    @Override
    public String change() {
    
    
        return "我是齐天大圣本尊!";
    }
}

Incarnation of the Great Sage:

public class DecoratorMonkeyChange implements MonkeyKing {
    
    

    public MonkeyKing king;

    public DecoratorMonkeyChange(MonkeyKing king) {
    
    
        this.king = king;
    }

    @Override
    public String change() {
    
    
        return this.king.change();
    }
}

The Avatar of the Great Sage:

public class DecoratorMonkeyChange1 extends DecoratorMonkeyChange {
    
    

    public DecoratorMonkeyChange1(MonkeyKing king) {
    
    
        super(king);
    }
    
    @Override
    public String change() {
    
    
        return this.king.change() + "变成了鱼!";
    }
}
public class DecoratorMonkeyChange2 extends DecoratorMonkeyChange {
    
    

    public DecoratorMonkeyChange2(MonkeyKing king) {
    
    
        super(king);
    }

    @Override
    public String change() {
    
    
        return this.king.change() + "变成了鸟!";
    }
}

Test category:

public class TestMonkey {
    
    
    public static void main(String[] args) {
    
    
        MonkeyKing king = new ConcreteMonkey();
        MonkeyKing a = new DecoratorMonkeyChange(king);
        MonkeyKing b = new DecoratorMonkeyChange1(a);
        MonkeyKing c = new DecoratorMonkeyChange2(b);
        System.out.println(c.change());
    }
}

Insert picture description here
Class Diagram:
Insert picture description here

Guess you like

Origin blog.csdn.net/qq_34365173/article/details/108058418