"Decorator mode" of design mode

Posted by Chef-Mars on Sat, 25 Dec 2021 20:50:24 +0100

Decorator mode

Definition: refers to the mode of dynamically adding some blame (i.e. adding its additional functions) to the object without changing the existing object structure. It belongs to the object structure mode.

  • Decorator is a powerful supplement to inheritance. It is more flexible than inheritance. It dynamically extends the function of an object without changing the original object, plug and play
  • Different effects can be achieved by using different decorative classes and the arrangement and combination of these decorative classes
  • The decorator mode fully complies with the opening and closing principle

Its main disadvantage is that the decorator pattern will add many subclasses, and overuse will increase the complexity of the program.

Pattern structure

Decorator mode mainly includes the following roles.

  1. Abstract Component role: define an abstract interface to standardize objects ready to receive additional responsibilities.
  2. Concrete component role: implement abstract components and add some responsibilities to them by decorating roles.
  3. Abstract Decorator role: inherits abstract components and contains instances of specific components. You can extend the functions of specific components through its subclasses.
  4. Concrete decorator role: implement relevant methods of abstract decoration and add additional responsibilities to specific component objects.

The structure diagram of decorator mode is shown in the figure:

case

Starbucks Coffee order project (CAFE):
1) Coffee type / single product: Espresso, ShortBlack, longblack, decaf
2) Seasoning: Milk, soy Milk, Chocolate
3) It is required to have good expansibility, easy modification and maintenance when expanding new coffee varieties
4) Use OO to calculate the cost of different kinds of coffee: customers can order single coffee or single coffee + seasoning combination.

Scheme I

The scheme design is shown in the figure:

problem analysis

  • Drink is an abstract class that represents a beverage.
  • des is a description of coffee, such as the name of coffee.
  • The cost() method is to calculate the cost and make an abstract method in the sink class.
  • Decaf is a single coffee. It inherits Drink and realizes cost.
  • Espress && milk is a single product of coffee + seasoning. There are many combinations.
  • In this design, there will be many types. When we add a single coffee or a new seasoning, the number will double, and there will be a class explosion.

Scheme II

According to the previous analysis, in scheme 1, because the combination of coffee single product + seasoning will double the number of classes, it can be changed to build seasoning into the Drink class, so as not to cause too many classes. So as to improve the maintainability of the project (as shown in the figure)

problem analysis

  • Scheme 2 can control the number of classes without causing many classes.
  • When adding or deleting a seasoning type, the maintenance of the code is very large.
  • Considering that users can add multiple condiments, you can return hasMilk to a corresponding int.
  • Consider using decorator mode

Scheme III (decoration mode)

The scheme design is shown in the figure:

analysis

Here, the subject and entity are roughly unchanged, and an intermediate layer is mainly added between the abstract subject and the seasoning. The function of the middle layer is to stack the expenses when calling cost. Another important thing is that the middle layer introduces a Drink object, which actually contains the decorated object. When calling cost, we will get its expenses, and then get all the expenses of this level through recursive release At the same time, the name of the specific decoration can be obtained through getDescription(). Even if there are so many things wrapped outside, it can also be obtained.

for example
Example order in decorator mode: 2 chocolate + 1 milk LongBlack

code implementation

abstract class

public abstract class Drink {

    public String des;//describe
    private float price = 0.0f;

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }
    //Abstract method of calculating cost
    //Subclasses
    public abstract float cost();
}

Here, an intermediate layer is added between the single coffee and the abstract class:

public class Coffee extends Drink{

    @Override
    public float cost() {
        return super.getPrice();
    }
}

Four types of single coffee realization:

public class Espresso extends Coffee{

    public Espresso(){
        setDes("Italian Coffee");
        setPrice(6.0f);
    }
}
public class LongBlack extends Coffee{
    public LongBlack(){
        setDes("longblack");
        setPrice(5.0f);
    }
}
public class ShortBlack extends Coffee {
    public ShortBlack(){
        setDes("shortblack");
        setPrice(4.0f);
    }
}
public class Decaf extends Coffee{
    public Decaf(){
        setDes("decaf");
        setPrice(7.0f);
    }
}

Combine single coffee and seasoning, and realize price calculation:

public class Decorator extends Drink{
    private Drink obj;

    public Decorator(Drink obj){//combination
        this.obj = obj;
    }

    @Override
    public float cost() {
        //getPrice own price
        return super.getPrice() + obj.cost();
    }

    @Override
    public String getDes() {
        //obj.getDes() outputs the information of the decorated person
        return super.des + " " + super.getPrice() +  " && " + obj.getDes();
    }
}

Three seasoning implementation classes:

public class Milk extends Decorator{

    public Milk(Drink obj) {
        super(obj);
        setDes("milk");
        setPrice(2.0f);
    }
}
public class Soy extends Decorator{
    public Soy(Drink obj) {
        super(obj);
        setDes("Soybean Milk");
        setPrice(1.5f);
    }
}
//Specific Decorator, here is the seasoning
public class Chocolate extends Decorator{

    public Chocolate(Drink obj) {
        super(obj);
        setDes("Chocolates");
        setPrice(3.0f);//Price of condiment
    }
}

Test class

public class CoffeeBar {
    public static void main(String[] args) {
        //Order in decorator mode: 2 chocolate + 1 milk LongBlack

        //1. Order a LongBlack
        Drink order = new LongBlack();
        System.out.println("Cost 1=" + order.cost());
        System.out.println("describe=" + order.getDes());

        //2. Add a portion of milk to order
        order = new Milk(order);
        System.out.println("order Add a milk charge=" + order.cost());
        System.out.println("order Add a milk description=" + order.getDes());

        //3. Add a chocolate order
        order = new Chocolate(order);
        System.out.println("order Add a portion of milk and a portion of chocolate=" + order.cost());
        System.out.println("order Add a portion of milk and a portion of chocolate=" + order.getDes());

        //4.order add another chocolate
        order = new Chocolate(order);
        System.out.println("order Add one milk, one chocolate and one chocolate=" + order.cost());
        System.out.println("order Add one milk, one chocolate and one chocolate=" + order.getDes());
    }
}

Topics: Java Back-end