Detailed explanation of design mode structural mode - decorator mode

Complete notes Directory: Contents of notes on detailed explanation of design pattern , welcome advice!

Structural mode describes how to form a larger structure of classes or objects according to a certain layout. There are two types:

  • Class structured pattern: uses inheritance mechanism to organize interfaces and classes.
  • Object structured pattern: use combination or aggregation to combine objects.

Because the coupling degree of composite relationship or aggregation relationship is lower than that of inheritance relationship and meets the "composite Reuse Principle", the object structured pattern has more flexibility than the class structured pattern.

The structural mode is divided into the following 7 types:

  • proxy pattern
  • Adapter mode
  • Decorator mode
  • Bridging mode
  • Appearance mode
  • Combination mode
  • Sharing mode

5.3 decorator mode

5.3.1 general

Let's take an example of a fast food restaurant: fast food restaurants have fried noodles, fried rice and other fast food. They can add side dishes such as eggs, ham and bacon. Of course, adding side dishes requires additional money. The price of each side dish is usually different, so it will be more troublesome to calculate the total price. The following is a class diagram using inheritance:

Problems with inheritance:

  • Poor scalability: if we want to add another ingredient (ham sausage), we will find that we need to define a subclass for FriedRice and FriedNoodles respectively. If you want to add a fast food category (fried rice noodles), you need to define more subclasses.
  • Too many subclasses generated

**Decorator mode: * * a mode that dynamically adds some responsibilities (i.e. additional functions) to the object without changing the existing object structure.

5.3.2 structure

Roles in Decorator mode:

  • Abstract Component role: define an abstract interface to standardize objects ready to receive additional responsibilities.
  • Concrete Component role: implement abstract components and add some responsibilities to them by decorating roles.
  • Abstract Decorator role: inherits or implements abstract components and contains instances of specific components. The functions of specific components can be extended through their subclasses.
  • Concrete decorator role: implement relevant methods of abstract decoration and add additional responsibilities to specific component objects.

5.3.3 cases

The decorator mode is used to improve the fast food restaurant case. The class diagram is as follows:

Abstract component role: fast food class

@Data
public abstract class FastFood {
    private float price; // Price
    private String desc; // describe

    public FastFood(float price, String desc) {
        this.price = price;
        this.desc = desc;
    }

    public abstract float cost();
}

Specific component roles: fried rice and fried noodles

public class FriedRice extends FastFood {
    public FriedRice() {
        super(10, "Fried rice");
    }

    public float cost() {
        return getPrice();
    }
}

public class FriedNoodles extends FastFood {
    public FriedNoodles() {
        super(12, "Stir-Fried Noodles with Vegetables");
    }

    public float cost() {
        return getPrice();
    }
}

Abstract decorator role: ingredient class

@Data
public abstract class Garnish extends FastFood {
    // Declare variables for fast food classes
    private FastFood fastFood;

    public Garnish(FastFood fastFood, float price, String desc) {
        super(price, desc);
        this.fastFood = fastFood;
    }
}

Specific decorator roles: egg ingredients and bacon ingredients

public class Egg extends Garnish {
    public Egg(FastFood fastFood) {
        super(fastFood, 1, "egg");
    }
  
    // Calculate price
    public float cost() {
        return getPrice() + getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}

public class Bacon extends Garnish {
    public Bacon(FastFood fastFood) {
        super(fastFood, 2, "Bacon");
    }
	
  	// Calculate price
    public float cost() {
        return getPrice() + getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}

Test class:

public class Client {
    public static void main(String[] args) {
        //  Order a fried rice
        FastFood food = new FriedRice();
        System.out.println(food.getDesc() + "  " + food.cost() + "element");
        // Add an egg to the fried rice above
        food = new Egg(food);
        System.out.println(food.getDesc() + "  " + food.cost() + "element");
        // Add another egg
        food = new Egg(food);
        System.out.println(food.getDesc() + "  " + food.cost() + "element");
        // Add another bacon
        food = new Bacon(food);
        System.out.println(food.getDesc() + "  " + food.cost() + "element");
    }
}
Fried rice 10.0 element
 Fried rice with eggs 11.0 element
 Fried rice with eggs 12.0 element
 Fried rice with bacon and eggs 14.0 element

5.3.4 usage scenarios

  • When the system cannot be extended by inheritance or inheritance is not conducive to system expansion and maintenance.

There are two main types of situations in which inheritance cannot be used:

  • The first is that there are a large number of independent extensions in the system. In order to support each combination, a large number of subclasses will be generated, resulting in an explosive increase in the number of subclasses.
  • The second type is because the class definition cannot inherit (such as final class).
  • Add responsibilities to a single object in a dynamic and transparent manner without affecting other objects.

  • When the functional requirements of the object can be added dynamically or revoked dynamically.

5.3.5 JDK source code analysis

The wrapper class in the IO stream uses the decorator mode: BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter.

Take BufferedWriter as an example. Let's see how to use BufferedWriter:

// Create FileWriter object
FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
// Create BufferedWriter object
BufferedWriter bw = new BufferedWriter(fw);
// Write data
bw.write("hello Buffered");
bw.close();

It really feels like a decorator mode. Next, let's look at their structure:

BufferedWriter uses decorator mode to enhance the Writer subclass and add buffer to improve the efficiency of writing data.

5.3.6 difference between agent and decorator

Similarities:

  • Must implement the same business interface as the target class
  • Declare the target object in both classes
  • Can enhance the target method without modifying the target class

difference:

  • Different purposes

    The decorator is to enhance the target object
    Static proxy is to protect and hide the target object

Decorators can be enhanced iteratively, and agents can only be enhanced once

  • Getting the target object is built differently
    The decorator is passed in from the outside, which can be passed through the construction method
    Static proxy is created inside the proxy class to hide the target object

Keywords: Java Design Pattern

Added by chipmunken on Thu, 20 Jan 2022 04:09:01 +0200