Decorator mode of Java design mode

Keywords: Java JDK

Decorator mode, which dynamically adds a series of actions to an object through this design mode, without the need to generate a large number of inheritance classes due to the differences in these actions.This pattern is almost ubiquitous in JDK, for example:
java.io.BufferedInputStream
java.io.FileReader
...
These classes are familiar enough to understand the decorator mode to better understand Java's IO system. Today, we'll take the Head First Design Mode as an example to learn what the decorator mode is like.

Previous Decorator Mode

Let's first see why this pattern is needed.
Originally, we wanted to expand our new capabilities by inheriting the parent class to produce new subclasses.Take the example of a coffee shop in a book. Drinks are basic types, drinks vary in name and practice, and will expand into a bunch of subcategories. As customer demand increases, the variety of drinks will also increase. If we look back at the system with a new variety and a subcategory, we will find that it simply explodes and there are many types that need to be maintained.Once the price of the ingredients has changed or the beverage formula needs to be improved, we have to modify the original classes.This practice violates one of our object-oriented design principles: classes should be open to extensions and closed to modifications.The original code may have been tested several times to make sure it works correctly, but once you modify the original code, you can't guarantee its certainty, so we should reject it as much as possible.Based on the above analysis, for beverages, we can design the most basic beverage, then add (decorate) the desired ingredients, and then commission to calculate the price of the new beverage (ingredient price + original price).Let's look at the book's definition of the decorator pattern:
Decorator mode dynamically attaches responsibility to objects.To extend functionality, decorators offer alternatives that are more resilient than inheritance.
The class diagram shows the following:

We can see that both ConcreteComponent and Decorator use inheritance to ensure that both the decorator and the decorated are of the same type. We use inheritance to ensure type matching rather than inheritance to obtain behavior.Where did that come from?The combination of decorators and components creates new behavior.Another benefit of using combinations is runtime dynamic expansion.Let's look directly at how this is done with code.

Decorator Mode of Life

Use the example from the book and hit it out for yourself.
(1) Beverage base

package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public abstract class Beverage {
    String desc = "UnKnown Beverage";

    public String getDesc() {
        return desc;
    }

    public abstract double cost();
}

(2) Condiment base class

package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public abstract class CondimentDecorator extends Beverage{
    public abstract String getDesc();
}

(3) Regular coffee

package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public class Espresso extends Beverage {

    public Espresso(){
        desc = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}

(4) Seasoning (milk and mocha)

package Decorator;

/**
 * Created by gray on 2017/9/24.
 */
public class Milk extends CondimentDecorator {
    Beverage beverage;
    public Milk(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public String getDesc() {
        return beverage.getDesc() + ", Milk";
    }

    @Override
    public double cost() {
        return 0.50 + beverage.cost();
    }
}
package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public class Mocha extends CondimentDecorator {
    Beverage beverage;

    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public String getDesc() {
        return beverage.getDesc() + ", Mocha";
    }

    @Override
    public double cost() {
        return 0.20 + beverage.cost();
    }
}

(5) Finally, test code and test results

package Decorator;

/**
 * Created by gray on 2017/9/24.
 */
public class Test {
    public static void main(String args[]){
        Beverage beverage = new Espresso();
        System.out.println("no Condiment : "+ beverage.getDesc() + " | total : $" + beverage.cost());

        Beverage beverage1 = new Mocha(new Espresso());
        System.out.println("have Mocha : " + beverage1.getDesc() + " | total : $" + beverage1.cost());

        Beverage beverage2 = new Milk(new Mocha(new Espresso()));
        System.out.println("have Milk,Mocha : " + beverage2.getDesc() + " | total : $" + beverage2.cost());
    }
}


Summary: Decorator mode is not very much to say. It is easy to understand the Decorator mode and see the related classes of I/O in jdk, most of which are Decorators.Decorator mode is more flexible than inheritance, but it also inevitably has many subclasses, which add complexity to the API. For all scenarios, when we need to dynamically add a new function or responsibility for an existing object, we can consider using Decorator mode. Also, as mentioned in the analysis above, subclasses will explode.Consider using the Decorator mode.

Posted by Mr.x on Wed, 22 May 2019 09:51:20 -0700