Decorator mode
1 Concept
It refers to the mode of dynamically adding a responsibility (adding other additional functions) to the object without changing the existing object structure.
2 Structure
Roles in decorator mode:
- Abstract build role: define an abstract interface to standardize objects ready to accept additional responsibilities
- Concrete role construction: implement Abstract construction and add some responsibilities to it by decorating roles
- Abstract decoration role: inherits or implements Abstract construction and contains instances of concrete construction. The functions of concrete construction can be extended through its subclasses
- Specific decoration role: implement the relevant methods of abstract decoration, and add additional responsibilities to the specific construction object
3 Implementation
//Fast food restaurant class (Abstract construction role) public abstract class FastFood { private float price; //Price private String desc; //describe public FastFood() { } public FastFood(float price, String desc) { this.price = price; this.desc = desc; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public abstract float cost(); }
//Fried rice (specific roles) public class FriedRice extends FastFood{ public FriedRice() { super(10, "Fried rice"); } @Override public float cost() { return getPrice(); } }
//Fried noodles (specific construction role) public class FriedNoodles extends FastFood{ public FriedNoodles() { super(12,"Stir-Fried Noodles with Vegetables"); } @Override public float cost() { return getPrice(); } }
//Decorator class (Abstract decorator role) public abstract class Garnish extends FastFood{ //Declare variables of fast food class private FastFood fastFood; public Garnish(float price, String desc, FastFood fastFood) { super(price, desc); this.fastFood = fastFood; } public FastFood getFastFood() { return fastFood; } public void setFastFood(FastFood fastFood) { this.fastFood = fastFood; } }
//Eggs (specific decorative role) public class Egg extends Garnish{ public Egg(FastFood fastFood) { super(1, "egg", fastFood); } @Override public float cost() { return getPrice() + getFastFood().cost(); } @Override public String getDesc() { return super.getDesc() + getFastFood().getDesc(); } }
//Bacon (specific decorative role) public class Bacon extends Garnish{ public Bacon(FastFood fastFood) { super(2, "Bacon", fastFood); } @Override public float cost() { return getPrice() + getFastFood().cost(); } @Override public String getDesc() { return super.getDesc() + getFastFood().getDesc(); } }
//Test class public class Test { public static void main(String[] args) { //Order a fried rice FastFood fastFood = new FriedRice(); System.out.println(fastFood.getDesc() + " " + fastFood.cost()); //Add an egg to the last fried rice fastFood = new Egg(fastFood); System.out.println(fastFood.getDesc() + " " + fastFood.cost()); //Add a bacon to the last fried rice fastFood = new Bacon(fastFood); System.out.println(fastFood.getDesc() + " " + fastFood.cost()); } }
Test results:
Fried rice 10.0
Fried rice with eggs 11.0
Fried rice with bacon and eggs 13.0
4 benefits
- Decorator mode 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
- The decorated class and the decorated class can develop independently without coupling each other. The decorator pattern is an alternative pattern of inheritance, and the decorator pattern can dynamically expand the functions of an implementation class
5 usage scenarios
- 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, which makes the number of subclasses increase explosively
- Add responsibility 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
6 JDK source code analysis
The wrapper classes in the IO stream use the decorator mode, BufferedInputStream, BufferedOutputStream, BufferedReader and BufferedWriter.
For example, BufferedWriter uses decorator mode to enhance the Writer sub implementation class, adds buffer, and improves the efficiency of writing data.
7 difference between static agent and decorator
Similarities:
- All of them should 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 obtain the target object: 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 in turn