Decorator mode of design mode (16)

Posted by FlashHeart on Fri, 26 Nov 2021 15:59:11 +0100

1, Starbucks Coffee order demand

         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, convenient modification and convenient 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.

1.1 scheme I

         Solution 1 - Analysis of Starbucks Coffee order:

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

2, Decorator mode

         Definition of Decorator Pattern: it refers to the pattern that dynamically adds some responsibilities (i.e. adds its additional functions) to the object without changing the existing object structure. It belongs to object structure pattern.

         The main advantages of decorator mode are:

  • 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 unnecessary 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.

2.1 principle of decorator mode

        Usually, the function of extending a class is implemented by inheritance. However, inheritance has static characteristics, high coupling, and subclasses will expand with the increase of extended functions. The goal of decorator pattern is to create a wrapper object (i.e. decoration object) to wrap the real object and provide it with additional functions on the premise of keeping the class structure of the real object unchanged. The basic structure and implementation method are analyzed below.

  1. Decorator mode is like packing a courier.
  2. Main body: for example: ceramics, clothes (Component) / / decorated person
  3. Packaging: for example: newspaper stuffing, plastic foam, cardboard, board (Decorator)

        The structure of the mode is as follows:

  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 Figure 1:

3, Decorator model solves Starbucks Coffee problem

         Scheme designed in decorator mode:

          For example, the order under decorator mode: 2 chocolate + 1 milk LongBlack, the specific code is as follows:

public class CoffeeBar {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 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. order add a portion of milk
        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. order add a chocolate

        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());

        // 3. order add a chocolate

        order = new Chocolate(order);

        System.out.println("order Add 1 milk and 2 chocolate =" + order.cost());
        System.out.println("order Add 1 part milk and 2 parts chocolate = " + order.getDes());

        System.out.println("===========================");

        Drink order2 = new DeCaf();

        System.out.println("order2 No coffee charge =" + order2.cost());
        System.out.println("order2 Coffee description = " + order2.getDes());

        order2 = new Milk(order2);

        System.out.println("order2 No charge for adding a portion of milk to coffee =" + order2.cost());
        System.out.println("order2 Add a milk description to the coffee = " + order2.getDes());


    }

}


//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
	}

}

// Concrete decorator role
public class Soy extends Decorator{

	public Soy(Drink obj) {
		super(obj);
		// TODO Auto-generated constructor stub
		setDes(" Soybean Milk  ");
		setPrice(1.5f);
	}

}

// Concrete decorator role
public class Milk extends Decorator {

	public Milk(Drink obj) {
		super(obj);
		// TODO Auto-generated constructor stub
		setDes(" milk ");
		setPrice(2.0f); 
	}

}

// Decorator
public class Decorator extends Drink {
	private Drink obj;
	
	public Decorator(Drink obj) { //combination
		// TODO Auto-generated constructor stub
		this.obj = obj;
	}
	
	@Override
	public float cost() {
		// TODO Auto-generated method stub
		// getPrice own price
		return super.getPrice() + obj.cost();
	}
	
	@Override
	public String getDes() {
		// TODO Auto-generated method stub
		// obj.getDes() outputs the information of the decorated person
		return des + " " + getPrice() + " && " + obj.getDes();
	}
	
	

}

public class DeCaf extends Coffee {

	public DeCaf() {
		setDes(" Coffee without cause ");
		setPrice(1.0f);
	}
}

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 Coffee  extends Drink {

	@Override
	public float cost() {
		// TODO Auto-generated method stub
		return super.getPrice();
	}

	
}

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();

}

        Then let's look at the running results:

          4, The use of decorator pattern in JDK source code

         The IO structure of Java, FilterInputStream, is a decorator.

  • public abstract class InputStream implements Closeable {} / / is an abstract class, Component.
  • Public class filterinputstream extensions InputStream {/ / is a Decorator class Decorator
public
class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;

    /**
     * Creates a <code>FilterInputStream</code>
     * by assigning the  argument <code>in</code>
     * to the field <code>this.in</code> so as
     * to remember it for later use.
     *
     * @param   in   the underlying input stream, or <code>null</code> if
     *          this instance is to be created without an underlying stream.
     */
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    ......

}

        Then there are many implementations under FilterInputStream. For example, DataInputStream is a specific decorator implementation.

Topics: Java Design Pattern