1. General
for instance
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.
The above figure is a class diagram using inheritance. Problems in using 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 pattern definition
Dynamically attach new functions to objects. In terms of object function extension, it is more flexible than inheritance. It 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 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.
3. Cases
Use the decorator model to improve the fast food restaurant case
The 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"); } }
benefit
- Decorator mode can bring more flexible extension functions than inheritance, and it is more convenient to use. You 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 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 an object can be added or revoked dynamically.
5.JDK source code analysis
The wrapper class in the IO stream uses decorator mode. BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter.
Let's take BufferedWriter as an example. First, let's see how to use BufferedWriter
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:
Summary
BufferedWriter uses decorator mode to enhance the Writer subclass and add buffer to improve the efficiency of writing data.
6. Difference between agent and decorator
Differences between static proxy and decorator modes:
- 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
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