8, Decorator mode

1. General

        Fast food restaurants have fried noodles and fried rice. They can add eggs, ham and bacon as side dishes. Of course, adding side dishes requires extra money. The price of each side dish is usually different, so it will be more troublesome to calculate the total price.

 

        Problems with inheritance:

        Poor scalability. If you want to add another ingredient (ham sausage), you will find that FiredRice and FiredNoddles need to define subclasses respectively. If you want to add a fast food category (fried rice noodles), you need to define more subclasses.

        Definition: refers to the mode of dynamically adding some responsibilities (i.e. adding additional functions) to the object without changing the existing object structure.

2. Structure

        Roles in Decorator mode:

        Abstract Component role: define an abstract interface to standardize and prepare several objects with 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 architectures. 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.

3. Cases

        Use the decorator model to improve the case of fast food restaurant and experience the essence of the decorator model. Class diagram is as follows:

The code is as follows:

 

//Fast food interface
public abstract class FastFood {
    private float price;
    private String desc;

    public FastFood() {
    }

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

    public void setPrice(float price) {
        this.price = price;
    }

    public float getPrice() {
        return price;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public abstract float cost();  //Get price
}

//Fried rice
public class FriedRice extends FastFood {

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

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

//Stir-Fried Noodles with Vegetables
public class FriedNoodles extends FastFood {

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

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

//Ingredients
public abstract class Garnish extends FastFood {

    private FastFood fastFood;

    public FastFood getFastFood() {
        return fastFood;
    }

    public void setFastFood(FastFood fastFood) {
        this.fastFood = fastFood;
    }

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

//Egg ingredients
public class Egg extends Garnish {

    public Egg(FastFood fastFood) {
        super(fastFood,1,"egg");
    }

    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }

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

//Bacon ingredients
public class Bacon extends Garnish {

    public Bacon(FastFood fastFood) {

        super(fastFood,2,"Bacon");
    }

    @Override
    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }

    @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();
        //Price spent
        System.out.println(food.getDesc() + " " + food.cost() + "element");

        System.out.println("========");
        //Order a fried rice with eggs
        FastFood food1 = new FriedRice();

        food1 = new Egg(food1);
        //Price spent
        System.out.println(food1.getDesc() + " " + food1.cost() + "element");

        System.out.println("========");
        //Order a fried noodles with bacon
        FastFood food2 = new FriedNoodles();
        food2 = new Bacon(food2);
        //Price spent
        System.out.println(food2.getDesc() + " " + food2.cost() + "element");
    }
}

advantage:

        Decorating pattern can bring more flexible extension functions than inheritance, and it is more convenient to use. It can obtain diversified results with different behavior states by combining different decorator objects. Decorator mode has better expansibility than inheritance, and perfectly follows the opening and closing principle. Inheritance is a static additional responsibility, while decorator is a dynamic additional responsibility.

        Decorative classes and decorated classes can develop independently and will not be coupled with each other. Decorative pattern is an alternative pattern of inheritance. Decorative pattern can dynamically expand the functions of an implementation class.

4. Usage scenario

        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 are generated, which makes the number of subclasses increase;

        The second type is because the class definition cannot inherit (such as final).

        Add responsibilities to a single object in a dynamic and transparent manner without affecting other objects;

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

5.JDK source code analysis

        The wrapper class in the IO stream uses decorator mode. BuffereddInputStream,BufferedOutputStream,BufferedReader,BufferedWriter‘.

        Take BufferedWriter as an example. BufferedWriter uses.

public class Demo {
    public static void main(String[] args) throws Exception{
        //Create BufferedWriter object
        //Create FileWriter object
        FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
        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, increase the buffer and improve the efficiency of writing data.

6. Difference between agent and decorator

        Differences between static proxy and decorator modes:

        Similarities:

        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.

        There are different places to get the target object component:

        Decorators are transmitted from the outside, which can be transmitted through the construction method;

        Static proxy is created inside the proxy class to hide the target object once.

Tags: Java Algorithm

Posted on Wed, 29 Sep 2021 17:48:07 -0400 by many_pets