“This is the 19th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

The decorator pattern is so widely used that even JDK source code makes it a gem, and many IO classes are best practices for the adapter pattern.

Definition 1.

The decorator pattern is a structural design pattern defined as “dynamically extending its functionality without changing the original class structure.”

2. The class diagram

In decorator mode, there are several roles:

  • Abstract Component: The core object of the decorator pattern, the primitive object, is usually an interface or abstract class
  • Concrete Component: An implementation class for an abstract Component that is targeted for decorator pattern decoration
  • Decorator: Combines abstract components and defines an abstract method to extend the capabilities provided by the abstract component, typically an abstract class
  • Concrete Decorator: Implements an abstract method provided by a Decorator, which is a Concrete extended implementation of the capabilities provided by an abstract component

Example 3.

Nowadays, many people like to drink milk tea. A cup a day is good for both body and mind (mainly the heart and mouth). When we drink milk tea, the waiter always asks what ingredients to add, which is the same as the ultimate daily conundrum: what to eat for dinner. The ingredients for milk tea addition may include Boba coconut pudding, etc. The milk tea with coconut fruit is finally labeled as coconut milk tea for sale, and the milk tea with boba is finally labeled as Boba milk tea for sale.

As an example of the decorator pattern, use milk tea shop to make milk tea today.

First, create an abstract class MilkyTea for making milk tea, which is an abstract component in the decorator pattern, and its code is as follows:

public abstract class MilkyTea {
    /**
     * 原料
     */
    public abstract String material(a);

    /**
     * 配料
     */
    public abstract String burden(a);

    /** * Milk tea detailed description, is decorated method */
    public String description(a) {
        return (this.burden() == null ? "" : this.burden()) + (this.material() == null ? "" : this.material()); }}Copy the code

Then create two specific components, namely coconut pudding milk tea and Boba Macchiato milk tea, the code is as follows:

public class Pudding extends MilkyTea {
    @Override
    public String material(a) {
        return "Pudding";
    }

    @Override
    public String burden(a) {
        return "Coconut"; }}public class Macchiato extends MilkyTea {
    @Override
    public String material(a) {
        return Macchiato;
    }

    @Override
    public String burden(a) {
        return "Pearl"; }}Copy the code

Then create a decorator class, the decorator role, which combines an abstract role and enhances the description method in the abstract role MilkyTea, with the following code:

public abstract class CustomMilkyTea {

    private MilkyTea milkyTea;

    public CustomMilkyTea(MilkyTea milkyTea) {
        this.milkyTea = milkyTea;
    }

    public abstract String extra(a);

    /** * extends its functionality **@return* /
    public String description(a) {
        return "Homemade" + this.extra() + milkyTea.description(); }}Copy the code

Then create two concrete decorative roles based on the two concrete roles, with the following code:

public class CustomPudding extends CustomMilkyTea {

    public CustomPudding(MilkyTea milkyTea) {
        super(milkyTea);
    }

    @Override
    public String extra(a) {
        return "Burning fairy grass"; }}public class CustomMacchiato extends CustomMilkyTea {

    public CustomMacchiato(MilkyTea milkyTea) {
        super(milkyTea);
    }

    @Override
    public String extra(a) {
        return "Caramel"; }}Copy the code

Finally, write the test code:

public class Test {

    public static void main(String[] args) {
        // Pudding and milk tea before decoration
        MilkyTea pudding = new Pudding();
        System.out.println(pudding.description());
        // For the decorated pudding and milk tea, you need to pass the decoration target into the decorator
        CustomMilkyTea customPudding = new CustomPudding(pudding);
        System.out.println(customPudding.description());

        // An undecorated Macchiato
        MilkyTea macchiato = new Macchiato();
        System.out.println(macchiato.description());
        // For the decorated Macchiato, you need to pass the decoration target into the decorator
        CustomMilkyTea customMacchiato = newCustomMacchiato(macchiato); System.out.println(customMacchiato.description()); }}Copy the code

The test output is as follows:

Homemade cauliflower pudding boba Macchiato Homemade caramel boba MacchiatoCopy the code

As you can see, the decorator classes CustomPudding and CustomMacchiato extend (enhance) the methods of the original class without changing Pudding and Macchiato.

4. Application scenarios

The use scenarios for the decorator pattern are:

  • You need to add functions to an object quickly and dynamically, and those functions can also be undone dynamically

5. Summary

This article described the decorator pattern, which is defined as dynamically extending its functionality without changing the original class structure.

The advantages of decorator mode are:

  • Dynamically add functionality to a single object without affecting other objects
  • Decorator and decorator classes are independent and easy to extend
  • Use composition instead of inheritance to achieve functional extension and reduce the existence of inherited classes

The disadvantages of the decorator pattern are:

  • If there are many layers of decoration, the code structure is complex and difficult to understand, as is the case with THE JDK’s IO classes

6. Reference materials

  • Zen of Design Patterns (2nd Edition)

Finally, this article is included in the Personal Speaker Knowledge Base: Back-end technology as I understand it, welcome to visit.