This paper introduces 23 design patterns of decoration patterns.
define
Sometimes we want to add functionality to an object rather than to the class as a whole, and while using inheritance is one way to add functionality, it is not flexible enough and can lead to subclasses adding useless functionality and being too coupled. A more flexible approach is to embed an object in another object, add functionality from that object, and call the embedded object decoration. This decoration corresponds to the interface of the decorated object, forwards requests to the decorated object, and does additional operations before and after forwarding. The decorator mode is to dynamically add some additional responsibilities to an object. The Decorator pattern is more flexible in terms of adding functionality than subclassing.
describe
- Pattern name: DECORATOR
- Type: Object structured schema
- Intent: To dynamically add some additional responsibilities to an object. The Decorator pattern is more flexible in terms of adding functionality than subclassing.
- Applicability:
- Add responsibilities to a single object dynamically and transparently without affecting other objects.
- Effect:
- Advantages:
- More flexible than inheritance.
- Avoid classes higher up in the hierarchy that have too many characteristics.
- Disadvantages:
- Multi-layer decoration is more complicated.
Let’s take a closer look at the decoration pattern with an example
A coffee shop, which is doing a lot of business, wants to update its order system to meet its current demand for drinks. The original design was as follows: There was an abstract class of drinks from which all drinks would inherit. The class contains the variable description, the method getDescription() to get the description, and the abstract method cost() to spend. Subclasses implement the cost method.Copy the code
In the case of a small variety of drinks, it is ok to add a variety of drinks in order to improve market competitiveness and upgrade consumption, and charge different fees according to the addition of different condiments. If we still create subclasses to achieve cost in the previous way, there will be too many subclasses, and if the price of a seasoning changes, there will be many classes to modify, which is not conducive to maintenance.
At this time, it may be said that it is better to directly add flavoring variables in the beverage class to set and obtain a method to determine whether there is a certain flavoring. The cost method in the beverage class is provided to realize the cost method for judging the price of flavoring. The subclass only needs to calculate its own value, which kind of seasoning is configured, and add the parent class
Public class Beverage {private String description; private double milkCost; private double soyCost; private double mochaCost; private double whipCost; private boolean hasMilk; private boolean hasSoy; private boolean hasMocha; private boolean hasWhip; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isHasMilk() { return hasMilk; } public void setHasMilk(boolean hasMilk) { this.hasMilk = hasMilk; } public boolean isHasSoy() { return hasSoy; } public void setHasSoy(boolean hasSoy) { this.hasSoy = hasSoy; } public boolean isHasMocha() { return hasMocha; } public void setHasMocha(boolean hasMocha) { this.hasMocha = hasMocha; } public boolean isHasWhip() { return hasWhip; } public void setHasWhip(boolean hasWhip) { this.hasWhip = hasWhip; } public double cost(){ double condimentCost = 0; if(hasMilk){ condimentCost+=milkCost; } if(hasMocha){ condimentCost+=mochaCost; } if(hasSoy){ condimentCost+=soyCost; } if(hasWhip){ condimentCost+= whipCost; } return condimentCost; }}Copy the code
public class DarkRoast extends Beverage { public DarkRoast() { setDescription("Most Excellent Dark Roast"); } @override public double cost() {return 2.3 + super.cost(); }}Copy the code
There are several problems with this approach:
- Condiment price change needs to modify the existing code
- Add or delete new condiments, need to change the corresponding variables and methods, as well as cost calculation.
- If a new drink does not fit the original condiment, the subclass will still have the useless condiment.
- If you want to double the seasoning, you also need to change the cost and add variables.
This approach clearly violates the open and closed principle of design: open for extension, closed for modification. In this case, it’s the price of each drink that changes, and the price needs to change depending on the condiment. Start with a drink object and decorate it with a spice object.
Decorates the pattern class diagram structure
It can be seen that:
- Decorator and decorator have the same parent class
- You can decorate objects with one or more decorator classes
- Decorator objects can be used instead of decorator class objects.
- Decoration objects can operate before and after the behavior of the decorator is performed.
Adjust according to decorator pattern
- Start with the beverage as the encapsulated
2. Garnish your drink with a mocha
3. Then take the object decorated by mocha as the decorated one, and decorate it with milk foam
So let’s look at the code
Public abstract class Beverage {public abstract String getDescription(); public abstract double cost(); }Copy the code
Decorator class
public class DarkRoast extends Beverage { @Override public String getDescription() { return "DarkRoast"; } @override public double cost() {return 2.3; }}Copy the code
Decorator’s parent class
public abstract class CondimentDecorator extends Beverage { private Beverage beverage; public CondimentDecorator (Beverage beverage){ this.beverage = beverage; } public Beverage getBeverage() { return beverage; }}Copy the code
Decorator implementation class
public class Mocha extends CondimentDecorator { public Mocha(Beverage beverage){ super(beverage); } @Override public String getDescription() { return getBeverage().getDescription()+" Mocha"; } @Override public double cost() { return getBeverage().cost()+.20; } } public class Whip extends CondimentDecorator { public Whip(Beverage beverage){ super(beverage); } @Override public String getDescription() { return getBeverage().getDescription()+" Whip"; } @Override public double cost() { return getBeverage().cost()+.10; }}Copy the code
Call the situation
public static void main(String[] args) { Beverage beverage = new DarkRoast(); System.out.println(beverage.getDescription()+" $ "+beverage.cost()); Mocha mocha = new Mocha(beverage); System.out.println(mocha.getDescription()+" $ "+mocha.cost()); Whip whip = new Whip(mocha); System.out.println(whip.getDescription()+" $ "+whip.cost()); } DarkRoast $2.3 DarkRoast Mocha $2.5 DarkRoast Mocha Whip $2.6Copy the code
Decorator pattern for Java I/O
The java.io package looks like it has a lot of classes, but it actually uses the decorator pattern
InputStream is the parent of decorator class and decorator class. FileInputStream, StringBufferInputStream, ByteArrayInputStream is the decorated class, FilterInputStream is the parent of the decorated class, BufferedInputStream, LineNumberInputStream, DataInputStream is the decorator class.