This paper first explains the content of decorator mode, and then goes deep into the content of decorator mode in java source code.
First, use an example in the head first design pattern book to explain the decorator pattern.
Now there is an abstract class called Beverage. Cost is used to calculate the total cost of various condiments.
public abstract class Beverage { protected String description = "beverage"; public abstract double cost(); public String getDescription() { return description; } }
There are many kinds of drinks, including Blue Mountain coffee, espresso, etc., and the spices added to different drinks are also different, including Mocha, cream, etc.
At this time, let you calculate the cost of various drinks. What would you do?
Many people will first think of the way of inheritance. For each drink, write a class to inherit the abstract class. Each class has its own different cost method to calculate the total cost of its own seasoning. But what's the problem? If I have many kinds of drinks, it is easy to cause class explosion.
What's more, someone wants to design a super class to inherit the abstract class, put the member variables of all spices in the super class and set them to Boolean values, and then judge and calculate each seasoning in the cost method. In this way, when subclasses inherit this superclass, as long as the Boolean value of seasoning is set, the cost method can automatically calculate the total cost.
This does solve the problem of class explosion, but it will bring new problems. The superclass contains all the spices, but not all the spices are needed in the subclass. Some spices are not even suitable for this drink, but the subclasses inherit them. Moreover, there is a very important problem that the opening and closing principle is not followed. If there is a new seasoning, you need to modify the superclass.
At this time, it's the decorator mode. It regards all kinds of drinks as decorators, all kinds of spices as decorators, and decorates drinks with all kinds of spices, similar to the following figure, just like Russian dolls.
Next, let's look at the specific implementation:
public class DarkRoast extends Beverage{ public DarkRoast() { description = "dark roast coffe"; } @Override public double cost() { return 2; } }
Darkroad stands for a drink. Its own cost is 2. It is the identity of the decorated person.
public abstract class CondimentDecorator extends Beverage{ @Override public abstract String getDescription(); }
The CondimentDecorator class is used for subclass declaration. Subclasses that inherit this class represent the identity of the decorator.
public class Mocha extends CondimentDecorator{ Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; } @Override public double cost() { return 0.2 + beverage.cost(); } @Override public String getDescription() { return beverage.getDescription() + ", Mocha"; } }
Mocha class represents a kind of seasoning. Its own cost is 0.2. It is the identity of the decorator. be careful! The important point here is that Mocha class combines the coverage class, which makes the code more flexible. Because inheritance is determined statically at compile time, while composition is determined dynamically at run time.
public class Whip extends CondimentDecorator{ Beverage beverage; public Whip(Beverage beverage) { this.beverage = beverage; } @Override public double cost() { return 0.1 + beverage.cost(); } @Override public String getDescription() { return beverage.getDescription() + ", Whip"; } }
Add a seasoning class Whip.
Now that we have drinks and spices, it's time to make a full cup of coffee
public class DecoratorMain { public static void main(String[] args) { Beverage darkRoast = new DarkRoast(); darkRoast = new Mocha(darkRoast); darkRoast = new Whip(darkRoast); System.out.println(darkRoast.getDescription() + ", cost:" + darkRoast.cost()); } } //Dark coast coffee, mocha, whip, cost: 13.0
As can be seen from the above code, it is very convenient to add seasoning to the beverage. Just new a seasoning class and pass in the decorated object. If you find it inconvenient to understand, you can manually debug it and have a look at the calling process. It's quite simple to understand.
This is a specific uml class diagram