07 decorator mode

Starbucks Coffee order project

Starbucks Coffee order project (CAFE):

● 1) coffee type / single product: Espresso, ShortBlack, LongBlack, Decaf
● 2) seasoning: Mik, Soy, 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

Solution 1 - solve Starbucks Coffee order project (poor solution)


Solution 1 - Analysis of Starbucks Coffee order

● 1) Drink is an abstract class that represents drinks
● 2) description is the description of coffee, such as the name of coffee
● 3) the cost method is to calculate the cost, and an abstract method is made in the Drink class
● 4) Decaf is a single coffee, inheriting Drink and realizing cost
● 5) espresso && milk is a single product of coffee + seasoning. There are many combinations
● 6) problem: in this design, there will be many types. When we add a single coffee or a new seasoning, the number of classes will double and there will be a class explosion

Solution 2 - solve Starbucks Coffee order project (better solution)

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 improved to build the 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)


Note: Milk, Soy and Chocolate can be designed as Boolean, indicating whether to add corresponding spices

Solution 2 - Analysis of Starbucks Coffee order

● 1) scheme 2 can control the number of classes without causing many classes
● 2) when adding or deleting seasoning types, there is a large amount of code maintenance
● 3) considering that the user can add multiple condiments, you can return hasMilk to a corresponding int
● 4) consider using decorator mode

Decorator mode

Decorator mode: dynamically attach new functions to objects. In terms of object function expansion, it is more flexible than inheritance. Decorator mode embodies the open close principle (OCP)

principle
● 1) decorator mode is like packing an express
○ main body: such as ceramics, clothes (Component)
Packaging: for example, newspaper stuffing, plastic foam, cardboard, board (Decorator)
● 2) Component: such as the Drink in front
● 3) concrete component: for example, the previous single coffee
● 4) Decorator: for example, each seasoning
● 4) between Component and ConcreteComponent, if there are many classes of ConcreteComponent, a buffer layer can be designed to extract the common parts and abstract them into a class

Decorator mode to solve Starbucks Coffee order project


explain

● 1) Drink is an abstract Component
● 2) ShortBlack single coffee is the specific subject
● 3) Decorator is a decoration class containing a decorated object (Drink)
● 4) the cost method of Decorator superimposes the cost and recursively calculates the price

Order in decorator mode: 2 chocolate + 1 milk LongBlack


explain

● 1) Milk includes LongBlack
● 2) one Chocolate contains Milk + LongBlack
● 3) one Chocolate contains Chocolate + Milk + LongBlack
● 4) in this way, no matter what form of single coffee + seasoning combination is, it can be easily combined and maintained through recursion

UML class diagram

Core code
/**
 * @ClassName Drink
 * @author: shouanzh Abstract subject
 * @Description Drink
 * @date 2022/1/19 22:30
 */
public abstract class Drink {

    // describe
    public String describe;

    private float price = 0.0f;

    public String getDescribe() {
        return describe;
    }

    public void setDescribe(String describe) {
        this.describe = describe;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    // Abstract method of calculating cost
    // Subclasses
    public abstract float cost();

}


/**
 * @ClassName Coffee
 * @author: shouanzh
 * @Description Coffee
 * @date 2022/1/19 22:36
 */
public class Coffee extends Drink{

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


public class Decaf extends Coffee {
    public Decaf() {
        setDescribe("Coffee without cause");
        setPrice(20.0f);
    }
}

public class Espresso extends Coffee {
    public Espresso() {
        setDescribe("Italian Espresso");
        setPrice(30.0f);
    }
}

public class LongBlack extends Coffee {
    public LongBlack() {
        setDescribe("Cafe Americano");
        setPrice(50.0f);
    }
}

public class ShortBlack extends Coffee {
    public ShortBlack() {
        setDescribe("Short black coffee");
        setPrice(40.0f);
    }
}

/**
 * @ClassName Decorator
 * @author: shouanzh
 * @Description Decorator
 * @date 2022/1/19 22:42
 */
public class Decorator extends Drink{

    private Drink drink;

    public Decorator(Drink drink) {
        this.drink = drink;
    }

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

    @Override
    public String getDescribe() {
        return super.getDescribe() + super.getPrice() + drink.getDescribe();
    }
}

/**
 * @ClassName Chocolate
 * @author: shouanzh
 * @Description Chocolate condiment
 * @date 2022/1/19 22:51
 */
public class Chocolate extends Decorator{
    public Chocolate(Drink drink) {
        super(drink);
        setDescribe("Chocolates");
        setPrice(6.0f);
    }
}

public class Soy extends Decorator {
    public Soy(Drink drink) {
        super(drink);
        setDescribe("Soybean Milk");
        setPrice(4.0f);
    }
}

public class Milk extends Decorator {
    public Milk(Drink drink) {
        super(drink);
        setDescribe("milk");
        setPrice(2.0f);
    }
}



/**
 * @ClassName CoffeeBar
 * @author: shouanzh
 * @Description CoffeeBar
 * @date 2022/1/19 22:55
 */
public class CoffeeBar {

    public static void main(String[] args) {

        // 2 chocolate + 1 milk LongBlack

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

        // 2. Add a portion of milk
        order = new Milk(order);
        System.out.println("A portion of milk + LongBlack price " + order.cost());

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

        // 4 add another portion of milk
        order = new Chocolate(order);
        System.out.println("A portion of milk + LongBlack + A chocolate + A chocolate price " + order.cost());

    }
}

JDK source code analysis

The IO structure of Java, FilterlnputStream, is a decorator

// Is an abstract class, namely Component
public abstract class InputStream implements Closeable {} 
// Is a decoration class, namely Decorator
public class FilterInputStream extends InputStream { 
    protected volatile InputStream in;
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
}
// FilterInputStream subclass also inherits the decorated object in
public class DataInputStream extends FilterInputStream implements DataInput { 
    public DataInputStream(InputStream in) {
        super(in);
    }

analysis
● 1) InputStream is an abstract class, similar to the Drink mentioned earlier
● 2) FileInputStream is a subclass of InputStream, similar to DeCaf and LongBlack
● 3) FilterInputStream is a subclass of InputStream, similar to the Decorator in front of us, modifier
● 4) DataInputStream is a subclass of FilterInputStream, which is similar to Milk, Soy, etc. above. The specific modifier is
● 5) FilterInputStream class has protected volatile InputStream in;, Including the decorated
● 6) the analysis shows that the IO system in JDK uses the decorator mode

Keywords: Design Pattern

Added by NoName.ba on Fri, 21 Jan 2022 14:58:57 +0200