introduce
Most office workers have the habit of sleeping in. They are very nervous at work every morning, so many people will solve the breakfast problem in a convenient way in order to get more sleep. Some people may have pancakes for breakfast. They can add eggs or sausages to the pancakes, but no matter how "overweight", they are still a pancake. In real life, it is often necessary to add new functions to existing products or beautify their appearance, such as house decoration, photos and photo frames, which are all decorator modes.
In the process of software development, sometimes you want to use some existing components. These components may only perform some core functions. However, without changing its structure, its function can be expanded dynamically. All of these can be implemented in decorator mode.
Definition and characteristics of decorator mode
Definition of Decorator Pattern: it refers to the pattern that dynamically adds some responsibilities (i.e. adds its additional functions) to the object without changing the existing object structure. It belongs to object structure pattern.
- Decorator is a powerful supplement to inheritance. It is more flexible than inheritance. It dynamically extends the function of an object without changing the original object, plug and play
- Different effects can be achieved by using different decorative classes and the arrangement and combination of these decorative classes
- The decorator mode fully complies with the opening and closing principle
Its main disadvantage is that the decorator pattern will add many subclasses, and overuse will increase the complexity of the program.
Structure and implementation of decorator mode
summary
Usually, the function of extending a class is implemented by inheritance. However, inheritance has static characteristics, high coupling, and subclasses will expand with the increase of extended functions. The goal of decorator pattern is to create a wrapper object (i.e. decoration object) to wrap the real object and provide it with additional functions on the premise of keeping the class structure of the real object unchanged. The basic structure and implementation method are analyzed below.
1. Structure of the model
Decorator mode mainly includes the following roles.
- 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 abstract components and contains instances of specific components. You can extend the functions of specific components through its subclasses.
- Concrete decorator role: implement relevant methods of abstract decoration and add additional responsibilities to specific component objects.
The structure diagram of the decorator mode is shown in Figure 1.
Fig. 1 structure diagram of decorator mode
2. Implementation of mode
The implementation code of decorator mode is as follows:
public class DecoratorPattern { public static void main(String[] args) { Component p = new ConcreteComponent(); p.operation(); System.out.println("---------------------------------"); Component d = new ConcreteDecorator(p); d.operation(); } } //Abstract component role interface Component { public void operation(); } //Specific component role class ConcreteComponent implements Component { public ConcreteComponent() { System.out.println("Create concrete component roles"); } public void operation() { System.out.println("Calling methods for specific component roles operation()"); } } //Abstract decorative role class Decorator implements Component { private Component component; public Decorator(Component component) { this.component = component; } public void operation() { component.operation(); } } //Specific decorative role class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } public void operation() { super.operation(); addedFunction(); } public void addedFunction() { System.out.println("Add additional functions to specific component roles addedFunction()"); } } /* The running results of the program are as follows: Create concrete component roles Call the method operation() of the specific component role --------------------------------- Call the method operation() of the specific component role Add additional functions for specific component roles addedFunction()*/
Code practice: solve the problem of Starbucks Coffee order project with decorator mode
Starbucks Coffee order project (CAFE):
1) Coffee type / single coffee: Espresso (Italian espresso), ShortBlack, longblack (American coffee)
2) Seasoning: Milk, soy Milk, Chocolate
3) It is required to have good expansibility, easy modification and maintenance when expanding new coffee varieties
4) Use OO to calculate the cost of different kinds of coffee: customers can order single coffee or single coffee + seasoning combination
The following figure is a clear structure diagram considered by the blogger:
The description clearly explains the structure of the whole diagram, but one class coffee is not mentioned. Coffee is a buffer layer designed to prevent too many types of single coffee in specific component roles, and can implement the abstract methods in the Drink class
(1) Create abstract component Drink
There are member variables des and price, which are the name, description and price of the beverage
There are also set/get methods and cost, an abstract method for calculating costs
public abstract class Drink { public String des; //describe private float price=0.0f; public String getDes() { return des; } public void setDes(String des) { this.des = des; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } //Abstract method of calculating cost //Subclasses public abstract float cost(); }
(2) Create the buffer layer Coffee class and implement the abstract method cost in Drink
public class Coffee extends Drink{ @Override public float cost() { return getPrice(); } }
(3) Create concrete components espresso (Italian espresso), ShortBlack and longblack (American coffee) to realize abstract components
Here I only show one of them. The others are roughly the same, but the values are different
public class LongBlack extends Coffee{ public LongBlack(){ setDes("longBlack"); setPrice(5.0f); } }
(4) Create an abstract decoration Decorator (here refers to seasoning), inherit abstract components and contain instances of specific components. You can extend the functions of specific components through its subclasses.
public class Decorator extends Drink{ private Drink obj; public Decorator(Drink obj){ this.obj=obj; } @Override public float cost() { //getPrice is the price of the seasoning itself return getPrice()+obj.cost(); } @Override public String getDes() { //obj.getDes() outputs the information of the decorated person return des+" "+ getPrice()+" && "+obj.getDes(); } }
(5) Create concrete decoration Milk, soy Milk and Chocolate, implement relevant methods of abstract decoration, and add additional responsibilities to specific component objects.
Only one of them is shown here. The other two are roughly the same, but the values are different
public class Chocolate extends Decorator{ public Chocolate(Drink obj) { super(obj); setDes("Chocolates"); setPrice(3.0f); } }
(6) Realize the order under decorator mode: two chocolate + one milk LongBlack
Be sure to add decorations later, so the first step is to click a (specific component) LongBlack
public class CoffeeBar { public static void main(String[] args) { //Order in decorator mode: two chocolate + one milk LongBlack //1. Order a LongBlack Drink order=new LongBlack(); System.out.println("Cost 1="+order.cost()); System.out.println("describe="+order.getDes()); //2.order add a portion of milk order = new Milk(order); System.out.println("Cost 2="+order.cost()); System.out.println("Description 2="+order.getDes()); //3. Add a chocolate order order=new Chocolate(order); System.out.println("Cost 3="+order.cost()); System.out.println("Description 3="+order.getDes()); //4. Add a chocolate order order=new Chocolate(order); System.out.println("Cost 4="+order.cost()); System.out.println("Description 4="+order.getDes()); } }
The print result is:
Cost 1 = 5.0
Description = longBlack
Cost 2 = 7.0
Description 2 = milk 2.0 & & longblack
Cost 3 = 10.0
Description 3 = chocolate 3.0 & & milk 2.0 & & longblack
Cost 4 = 13.0
Description 4 = chocolate 3.0 & & Chocolate 3.0 & & milk 2.0 & & longblack