Decorator mode of design mode

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.

  1. Abstract Component role: define an abstract interface to standardize objects ready to receive additional responsibilities.
  2. Concrete component role: implement abstract components and add some responsibilities to them by decorating roles.
  3. Abstract Decorator role: inherits abstract components and contains instances of specific components. You can extend the functions of specific components through its subclasses.
  4. 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

Added by dubc07 on Tue, 09 Nov 2021 09:14:05 +0200