I walk very slowly, but I never walk backwards
Copy the code
Design Mode – Decorator mode
Become as
Hello everyone, I am silent. In this lesson, we will talk about the decorator mode in the design mode. Of course, first of all, have a cup of coffee by the Seine River, the left bank ☕️
Case requirements – Starbucks coffee
Take a look at a Starbucks coffee order project requirements:
-
Coffee type/individual coffee: Espresso, Cappuccino, Cafe Latte
-
Ingredients: Milk, sugar
Customers can order single coffee, or single coffee + ingredients, according to the customer order to calculate the cost of different types of coffee
Requirements in the expansion of new coffee types, with good scalability, easy to change, easy to maintain
Solution 1: General implementation
In view of the above requirements, we generally think of the implementation method is to define a base class Coffee, and then let all kinds of single Coffee inherit the base class Coffee, rewrite the description() and cost() methods in it, of course, Coffee can also add things, we can also use this method, It’s a combination of coffee and each of the ingredients, and it looks like this:
Actually this kind of thinking we don’t need to be realized, it would be easy to find, to do this, maintainability and scalability of the entire project is very poor, if the coffee shop with a new kind of sheet is tasted coffee, number multiplication, because coffee can also add something, that would be a kind of explosion problem, so this method can implement the business logic, However, considering the expansion and maintenance is too poor to meet the needs, so it is not desirable
Solution two: ingredient improvement
OK, the previous analysis, we use solution a Coffee order project, because customers can order sheet is tasted Coffee with any ingredients, so the class plan for a while due to the use of multiplication, scalability and maintainability are very poor, so we need to improve, that some friend said, we can put the ingredients in the class built into the Coffee, So instead of multiplying the number of classes, let’s look at a simple class diagram
For example, the hasSugar() method returns an int type. Then rewrite this method for a single Coffee, and return 0 to indicate that no sugar is added. Return 1 or some other amount of sugar added, and then the cost() method completes the billing, so that the number of classes will not double, add a class to the new single coffee, and the maintainability of the project will be improved
Project analysis
In fact, scheme 2 also has some problems. Although scheme 2 controls the number of classes so as not to cause class explosion, for ingredients, according to the idea of Scheme 2, each ingredient needs to provide has() and set() methods. Considering the fact that there are many ingredients that can be added to coffee, The amount of code is still very large when it comes to the maintenance (CRUD) of ingredients. Therefore, although plan 2 has a great improvement in scalability and maintenance compared with Plan 1, it is not the optimal solution for the coffee order project. After so long preparation, we can introduce our leading role – decorator mode here
Basic introduction
Decorator pattern: Dynamically attaching functionality to objects without changing the original object, providing a more flexible alternative to inheritance (extending the functionality of the original object). Decorator pattern also embodies the open closed principle (OCP).
The principle of dynamically attaching new functionality to objects and OCP mentioned here will be shown in code in later application examples
The principle of class diagram
In fact, the above concept is quite abstract. Let’s change the way of thinking and combine the principle class diagram of decorator pattern to understand decorator pattern
The decorator mode is similar to packing an express. For example, we need to pack and mail a laptop computer to a friend, which cannot be directly mailed. It needs to be packed in a carton and the express bag is wrapped outside. Cardboard boxes and delivery bags are packaging – decorators are decorators, so we can abstract some of the roles of the Decorator pattern based on the class diagram
Decorator mode character
-
Component Body: Defines a template for a body, analogous to the base class Coffee for the Previous Starbucks project
-
ConcreteComponent: Concrete bodies, analogous to the individual coffees above
-
Decorator: similar to the various ingredients in coffee. (As you can see from the class diagram, the Decorator has the subject in it.
-
ConcreteDecoratorA /B: Concrete decorator role, responsible for concrete decorator details
Of course, between the Component and ConcreteComponent in the figure, if there are many ConcreteComponent classes, you can also create a buffer layer that extracts the common parts and abstracts out another layer
Solution 3: Decorator pattern
Class diagrams show
The abstract base class Coffee is the principal in the decorator pattern role
Espresso, ConcreteComponent, etc
Decorators are decorators that aggregate the Decorator Coffee
The Decorator’s cost() method adds costs recursively
Decorator mode order thinking
Why do we need recursion? Let’s say the customer orders a coffee, a cappuccino with one milk and two sugar, and that’s the idea
1) Inside, Milk contains Cappuccino, sugar contains Milk + Cappuccino
Cappuccino, Cappuccino, Cappuccino, Cappuccino
3) In this way, no matter what form of single coffee plus ingredients, it can be easily combined and maintained by recursive method
Code demo
Public class Coffee {private String desc; Private float price = 0.0f; public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } // Public abstract float cost(); } public class Cappuccino extends Coffee {public Cappuccino(){setDesc(" Cappuccino "); SetPrice (24.0 f); } @Override public float cost() { return super.getPrice(); }} public class Espresso extends Coffee{public Espresso(){setDesc(" Espresso "); SetPrice (18.0 f); } @Override public float cost() { return super.getPrice(); }} public class extends Coffee {// Aggregate private Coffee; public Decorator(Coffee coffee){ this.coffee = coffee; } @override public float cost() {return super.getprice () + coffee.cost(); } @Override public String getDesc() { return super.getDesc() + coffee.getDesc(); }} public class Milk extends Decorator{public Milk(Coffee) {super(Coffee); SetDesc (" milk "); SetPrice (3.0 f); Public class Sugar extends Decorator {public Sugar(Coffee) {super(Coffee); SetDesc (" sugar "); SetPrice (2.0 f); }} public class CoffeeStore {public static void main(String[] args) {public static void main(String[] args) Coffee order = new Cappuccino(); // system.out.println (order.cost())); // System.out.println(order.getDesc()); Order = new Milk(order); // System.out.println(order.cost()); // // System.out.println(order.getDesc()); Order = new Sugar(order); // System.out.println(order.cost()); // System.out.println(order.getDesc()); Order = new Sugar(order); System.out.println(order.cost()); System.out.println(order.getDesc()); }}Copy the code
Design advantage
Under the current mode, if we want to add a single Coffee, you will find that inheriting the Coffee class can be used, as well as adding a new ingredient. The expansibility of this design is very excellent and flexible. I can order or add various ingredients at will, no matter how many combinations. It is one of the better solutions to complete starbucks coffee orders
Decorator pattern vs. inheritance
-
Decorator pattern with the purpose of inheritance is to extend the functionality of objects, but the decorator pattern provides more flexibility than inheritance, the decorator pattern allows the system to the dynamic decision “with a” need “decoration”, or remove a don’t need “adornment, inheritance is different, inheritance is static, it determines, in front of the system is running
-
If the decorator pattern, relative to inherit, will greatly reduce the number of need to class, because if you are using inheritance method, so each an all need a class that can cause a large number of repeat performance of the class, of course, on the other hand, the use of decorative pattern produces more objects than using inheritance relationship
JDK – IO source code analysis
Decorator pattern in the Java language, the most famous application is the design of the Java I/O standard library, here, let’s take the application of the decorator pattern in IO source, because the Java I/O library need a lot of the various combinations of performance, if the performance is done with inherited methods, then each an all need a class, This results in a large number of classes with repetitive performance, and the decorator pattern is the basic pattern of the Java I/O library because the number of classes is greatly reduced and the repetition of performance is minimized with the decorator pattern
In Java’S IO structure, FilterInputStream plays the role of decorator, as shown in the diagram below
Below I write a section of test code, into the source code to comb through the use of decorator mode process
public class Test { public static void main(String[] args) throws Exception{ DataInputStream dis = new DataInputStream(new FileInputStream("d:\\jiran.txt")); dis.read(); dis.close(); }}Copy the code
A partial copy of the source code is displayed in a code block, as shown in the figure
// As you can see, FileInputStream is a subclass of InputStream public class FileInputStream extends InputStream {/* File Descriptor - handle to the open file */ private final FileDescriptor fd; Public abstract class InputStream implements Closeable {// FilterInputStream internally aggregates InputStream, Public class FilterInputStream extends InputStream {/** * The input stream to be filtered. */ protected volatile InputStream in; // As you can see, Public class DataInputStream extends FilterInputStream implements DataInput {DataInputStream implements DataInputStreamCopy the code
The source code that
-
The abstract class InputStream is analogous to the decorated Coffee in the case of Starbucks
-
FileInputStream is a subclass of InputStream, which is the specific subject of various single coffee in the case of Starbucks
-
FilterInputStream internally aggregates InputStream, acting as Decorator, analogous to the Starbucks Decorator
-
DataInputStream is a subclass of FilterInputStream, which is a concrete decorator, analogous to Milk/Sugar in the Starbucks case
So, in the IO architecture of the JDK, decorator pattern is used
Next day forecast
OK, here, decorator mode related content is over, next section, we start the study of combination mode, I hope we can stick to it together, really harvest, just like the sentence at the beginning, I walk slowly, but I never backward, ha ha, then we will see you next time ~