Decorator mode of design mode

Starbucks Coffee order item

  1. The types of single coffee are Espresson, ShortBlack, LongBlack and Decaf.
  2. Seasoning: Milk, 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 types of development: customers can order single coffee or single coffee + seasoning.
Scheme I. principle and coffee combination


The most obvious problem in the traditional intuitive way to solve this requirement is the explosive increase of classes. It is not conducive to product maintenance and iteration. Because each addition of a principle may involve a combination with other raw materials and coffee, which may be fully arranged. After adding a principle or adding a new variety of development, development is a nightmare.

Scheme 2: combine raw materials in Drink class


Put the ingredients in the Drink class and order coffee. If we need raw materials, we will call the set method of raw materials and set the corresponding raw materials. You don't need to set the original time. In this way, the problem of class explosion is solved. However, it violates the opening and closing principle - expanding the opening and modifying the closing principle. For example, when a new material is added, we need to modify the code of the Drink class. When a material is deleted, we need classes such as ShortBlack, LongBlack, Espresso and Decaf, and the code maintenance workload is also heavy.

Decorator mode

definition

Decorator mode: dynamically attach new functions to objects. In terms of object function extension, it is more flexible than inheritance. The decorator mode also embodies the opening and closing principle.

Decorator mode principle

  1. Component body: similar to the previous sink class
  2. Concrete component is a specific body: for example, the single coffee in front is decorated.
  3. Decorator: such as various spices.
Decorator mode to solve the coffee problem
  1. UML class diagram
  2. Order placement in decorator mode: 2 chocolate + 1 milk LongBlack coffee

    The main body is LongBlack, that is, the decorator, while Milk and Chocolate are decorators to wrap LongBlack.
  3. code implementation
    Sink class code:
    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
        public abstract float cost();
    }
    
    Coffee class (abstract body) code:
    public class Coffee extends Drink {
        @Override
        public float cost() {
            return super.getPrice();
        }
    }
    
    LongBlack, Espresson and ShortBlack (specific entity) codes:
    public class LongBlack extends Coffee {
    
        public LongBlack() {
            setDes("longBlack");
            setPrice(5.0f);
        }
    }
    
    public class Espresso extends Coffee {
    
        public Espresso() {
            setDes("Italian Coffee");
            setPrice(6.0f);
        }
    }
    
    public class ShortBlack extends Coffee {
    
        public ShortBlack() {
            setDes("shortBlack");
            setPrice(4.0f);
        }
    }
    
    Decorator (decorator abstraction layer) class code:
    public class Decorator extends Drink {
    
        private Drink obj;
    
        public Decorator(Drink obj) {
            this.obj = obj;
        }
        @Override
        public float cost() {
            return super.getPrice() + obj.cost();
        }
        @Override
        public String getDes() {
            return super.getDes() + " " + super.getPrice() + "&&" + obj.getDes();
        }
    }
    
    Chocolate class, Milk class and Soy class (specific decorator) code:
    public class Chocolate extends Decorator {
    
        public Chocolate(Drink obj) {
            super(obj);
            setDes("Chocolates");
            setPrice(3.5f); // Price of condiment
        }
    }
    
    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("soy");
            setPrice(1.5f);
        }
    }
    
    Client class CoffeeBar class:
    public class CoffeeBar {
    
        public static void main(String[] args) {
    
            // 1. Order a LongBlack
            Drink order = new LongBlack();
            System.out.println("Cost 1=" + order.cost());
            System.out.println("Description 1=" + order.getDes());
    
            // 2. Add a portion of milk
            order = new Milk(order);
            System.out.println("Cost 2=" + order.cost());
            System.out.println("Description 2=" + order.getDes());
    
            // 3. Add a chocolate
            order = new Chocolate(order);
            System.out.println("Cost 3=" + order.cost());
            System.out.println("Description 3=" + order.getDes());
        }
    }
    

Source code analysis of decorator mode in JDK application

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

FilterInputStream class, StringBufferInputStream class and ByteArrayInputStream class are equivalent to the concrete body of the ConcreteComponent in the decorator mode, while FilterInputStream class is the abstract layer of the decorator, and BufferInputStream, DataInputStream and LineNumberInputStream are equivalent to the concrete decorator.
FilterInputStream source code:

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;
    }
    ...
}

From the source code, we can see that FilterInputStream inherits InputStream and aggregates the InputStream class.

Tags: Design Pattern

Posted on Tue, 21 Sep 2021 01:39:28 -0400 by Waxxy