Grinding 23 kinds of big talk design modes ----- decorator mode (decorator mode)

Keywords: Java Database Big Data Design Pattern

Hello, everyone. I'm a student in the java learning circle who doesn't want to disclose my name and lives a shoddy life. If there are mistakes in the article, I hope Haihan. You're welcome to make more corrections

If you get useful dry goods knowledge from this article, please give me a favor. It is said that all the likes have got offer s

Template method pattern

Introduction:

Decorator Pattern allows you to add new functionality to an existing object without changing its structure.
This type of design pattern belongs to structural pattern. It is a package of existing classes, and can realize the mixing of different decorative functions on the basis of the original functions.
This pattern creates a decoration class to wrap the original class, and provides additional functions on the premise of maintaining the integrity of class method signature.
We use the following example to demonstrate the use of decorator pattern. For example, we will decorate a shape with different colors without changing the shape class.
For another example, grab cake is based on cake, and then added with decoration (such as vegetable leaves, sausage, meat slices)
Then the above-mentioned decorative mixing comes. I can realize a cake + vegetable leaves, a cake plus sausage, or a cake + sausage + meat slice
If you still have an impression of the arrangement and combination of high school, this decorative mix is the case
However, the result of each arrangement and combination must contain at least one original basic "cake"

Extended case

The finance department calculates the monthly bonus for the employees of the sales department. The employee bonus is divided into three categories (the manager is also an employee, and the employee is also a worker in the cow)

  1. Default bonus (regardless of sales): sales * 5%
  2. For employees with sales of more than 7000, the bonus is: 2% on the basis of the first
  3. The bonus of the sales manager is: the bonus of himself as an employee + the sales of all personnel of the sales department (including himself) * 1%

case analysis

Object in the case: Finance (used to calculate bonus)
In addition, employee information is stored in a separate class, which acts as a database (simulating a query process, of course, this is not the point. Well, let's write this case first, find out what's wrong and need to be optimized, and then lead to the play decorator model)

Case code

Employee category

package decorator.nopattern;

/**
 * @author xingyu
 * @date 2021/9/8 9:09
 * staff
 */
public class Staff {

    private Long id;
    //sales volume
    private Double saleroom;
    //Employee identity: suppose "ordinary" means employee and "manager" means Sales Manager (two identities)
    private String identity;

    public Staff(Long id, Double saleroom, String identity) {
        this.id = id;
        this.saleroom = saleroom;
        this.identity = identity;
    }

    public Long getId() {
        return id;
    }

    public Double getSaleroom() {
        return saleroom;
    }

    public String getIdentity() {
        return identity;
    }
}

The StaffDatabase class acts as a database

package decorator.nopattern;

import java.util.HashMap;
import java.util.Map;

/**
 * @author xingyu
 * @date 2021/9/8 9:07
 * StaffDatabase((employee database)
 * This kind of simulation database
 * Query data from database
 */
public class StaffDatabase {

    /**
     * Map Represents an employee table
     * Use long to prevent too many employees and insufficient int
     * This is the knowledge in the database and will not be repeated. Just understand the function of this class
     * It doesn't matter if you don't understand. Just look down
     */
    private static Map<Long,Staff> staffMap;

    /**
     * Initialization data
     */
    static {
        staffMap = new HashMap<>();
        staffMap.put(1L,new Staff(1L, 6000.0, "ordinary"));
        staffMap.put(2L,new Staff(2L, 8000.0, "ordinary"));
        staffMap.put(3L,new Staff(3L, 10000.0, "manager"));
    }

    /**
     * Query employee information according to employee id
     * @param id
     * @return
     */
    public static Staff selectOne(Long id){
        return staffMap.get(id);
    }

    /**
     * Query the information of all employees
     * @return
     */
    public static Map<Long,Staff> selectAll(){
        return staffMap;
    }
}

Finance (used to calculate employee bonus)

Because there are three ways to calculate the bonus, according to the principle of single responsibility (simply understood as one class doing its own thing and one method doing its own thing), three methods are divided to do its own thing

package decorator.nopattern;

import decorator.withpattern.Rule;

import java.util.Map;

/**
 * @author xingyu
 * @date 2021/9/8 9:01
 * Finance((Finance)
 * Be responsible for calculating the salary of each employee
 * Primitive class (why is it called primitive class? It will be mentioned later)
 */
public class Finance {

    /**
     * Calculate the employee's bonus according to the employee's number (primary key - term in the database, indicating uniqueness)
     */
    public double calculateBonus(Long id){
        double bonus = 0;
        bonus += calculateOne(id);
        bonus += calculateTwo(id);
        bonus += calculateThree(id);
        System.out.println("id by " + id + " The total bonus for employees is:" + bonus);
        return bonus;
    }

    /**
     * Calculate the bonus of the first category [50007000)
     */
    private double calculateOne(Long id){
        double res = 0;
        Staff staff = StaffDatabase.selectOne(id);
        double baseSalary = staff.getSaleroom();
        res = baseSalary * 0.05;
        System.out.println("id by " + id + " The first type of bonus for employees is:" + res);
        return res;
    }

    /**
     * Calculate the bonus of category II (sales greater than)
     */
    private double calculateTwo(Long id){
        double res = 0;
        Staff staff = StaffDatabase.selectOne(id);
        double baseSalary = staff.getSaleroom();
        if(baseSalary >= 7000){
            res = baseSalary * 0.02;
        }
        System.out.println("id by " + id + " The second type of bonus for employees is:" + res);
        return res;
    }

    /**
     * Calculate the manager's bonus other than personal bonus
     */
    private double calculateThree(Long id){
        double res = 0;
        Staff staff = StaffDatabase.selectOne(id);
        if("manager".equals(staff.getIdentity())){
            double sum = 0;
            Map<Long,Staff> staffMap = StaffDatabase.selectAll();
            for (Long curId : staffMap.keySet()){
                Staff curStaff = StaffDatabase.selectOne(curId);
                sum += curStaff.getSaleroom();
            }
            res = sum * 0.01;
        }
        System.out.println("id by " + id + " In addition to the individual bonus, the bonus of the manager is:" + res);
        return res;
    }
}

Main method test

package decorator;

import decorator.nopattern.Finance;
import decorator.withpattern.Decorator;
import decorator.withpattern.DecoratorOne;
import decorator.withpattern.DecoratorThree;
import decorator.withpattern.DecoratorTwo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;

/**
 * @author xingyu
 * @date 2021/9/7 20:34
 */
@SuppressWarnings("all")
public class DecoratorTest {

    public static void main(String[] args) {
    	//Create financial object
        Finance finance = new Finance();
        //Let the financial department calculate the bonus of the employee with id 3
        finance.calculateBonus(3L);
    }
}

results of enforcement

What's wrong with analyzing the case code

What's wrong with thinking?
The scalability is not good, because the calculation method of bonus may be modified, which will cause the problem of modifying the source code
Another is to add a new bonus calculation method. For example, the third category: if the employee bonus for sales [50007000) is sales * 3%
Or delete a certain bonus calculation method, but also change the source code, which violates the opening and closing principle of the design
Therefore, the urgent problem to be solved now is: how to dynamically calculate an employee's bonus?
This leads to the decorator mode
Create decorator classes. Each decorator class is responsible for the calculation of a bonus, which is accumulated based on the previous bonus
How to accumulate the bonus calculated before?
In this decorator class, add the decorator object of the last bonus calculation as the property
give an example:
The original calculated bonus is 0. If the decorator one selects the first type of bonus calculation method for the first time, the class adds the object for calculating the original class (that is, as soon as we open the financial class, the financial bonus calculation method changes) as the attribute. First calculate 0, and then calculate the result of the first type of bonus and 0 accumulation

Optimize case code with decorator mode

First, as a simulated database, the StaffDatabase class does not need to be changed
The decorator can hold objects of financial class as attributes, and the decorator can hold other decorator objects as attributes
Because the decoration needs to realize the mixed effect (any decoration can be added), the type of financial objects or other decorator objects installed in a decorator cannot be written dead as an attribute, but polymorphism can be realized through rules, so that the mixed effect can be realized

Rule (a top-level rule that both the decorator and the decorator need to follow)

In the financial class (original class), other decorator objects will not be used as attributes, and he is not a decorator himself, because he is decorated by decorators, just like the "cake" in the hand grasping cake mentioned earlier
If you don't understand why you need this rule, you may suddenly see the subsequent code

package decorator.withpattern;

/**
 * @author xingyu
 * @date 2021/9/8 10:19
 * Unify the method names of the original class and decorator, and enable the first decorator to add the object of the original class as an attribute
 * It can also confuse the user's vision and can't distinguish which class of methods are called
 */
public abstract class Rule {

    public abstract double calculateBonus(Long id);
}

Decorator rules

In order to achieve the mixed effect, the objects of different decorator classes can be used as properties instead of a write dead type in the decorator class

package decorator.withpattern;

/**
 * @author xingyu
 * @date 2021/9/8 10:11
 * Decorator rules
 */
public abstract class Decorator extends Rule {

    /**
     * The previous decorator object needs to be used by the current class object (that is, this object needs to be used by subclasses), so use protected
     */
    protected Rule rule;
    public Decorator(Rule rule) {
        this.rule = rule;
    }

    /**
     * Calculate the bonus at this time = current bonus + previous bonus
     * @return
     */
    public abstract double calculateBonus(Long id);
}

Calculation of category I bonus (decorator)

package decorator.withpattern;

import decorator.nopattern.Staff;
import decorator.nopattern.StaffDatabase;

/**
 * @author xingyu
 * @date 2021/9/8 10:14
 * This decorator is used to calculate the bonus of the first category (default bonus (regardless of sales): sales * 5%)
 */
public class DecoratorOne extends Decorator {

    public DecoratorOne(Rule rule) {
        super(rule);
    }

    @Override
    public double calculateBonus(Long id) {
    	//Call the decorator method of the previous decorator (or maybe not the decorator but the original class) first
        double preBonus = rule.calculateBonus(id);
        double res = 0;
        Staff staff = StaffDatabase.selectOne(id);
        double baseSalary = staff.getSaleroom();
        res = baseSalary * 0.05;
        System.out.println("add to " + this.getClass().getSimpleName() + " The bonus after decorating is:" + (preBonus + res));
        return preBonus + res;
    }
}

Calculation of type II bonus (decorator)

package decorator.withpattern;

import decorator.nopattern.Staff;
import decorator.nopattern.StaffDatabase;

/**
 * @author xingyu
 * @date 2021/9/8 10:34
 * This decorator is used to calculate the second type of bonus (for employees with sales of more than 7000, the bonus is: add 2% on the basis of the first type)
 */
public class DecoratorTwo extends Decorator {

    public DecoratorTwo(Rule rule) {
        super(rule);
    }

    @Override
    public double calculateBonus(Long id) {
    	//Call the decorator method of the previous decorator (or maybe not the decorator but the original class) first
        double preBonus = rule.calculateBonus(id);
        double res = 0;
        Staff staff = StaffDatabase.selectOne(id);
        double baseSalary = staff.getSaleroom();
        if(baseSalary >= 7000){
            res = baseSalary * 0.02;
        }
        System.out.println("add to " + this.getClass().getSimpleName() + " The bonus after decorating is:" + (preBonus + res));
        return preBonus + res;
    }
}

Calculation of category III bonus (decorator)

package decorator.withpattern;

import decorator.nopattern.Staff;
import decorator.nopattern.StaffDatabase;

import java.util.Map;

/**
 * @author xingyu
 * @date 2021/9/8 10:36
 * Calculate the manager's bonus other than personal bonus (sales of all personnel of the sales department (including themselves) * 1%)
 */
public class DecoratorThree extends Decorator {

    public DecoratorThree(Rule rule) {
        super(rule);
    }

    @Override
    public double calculateBonus(Long id) {
    	//Call the decorator method of the previous decorator (or maybe not the decorator but the original class) first
        double preBonus = rule.calculateBonus(id);
        double res = 0;
        Staff staff = StaffDatabase.selectOne(id);
        if("manager".equals(staff.getIdentity())){
            double sum = 0;
            Map<Long,Staff> staffMap = StaffDatabase.selectAll();
            for (Long curId : staffMap.keySet()){
                Staff curStaff = StaffDatabase.selectOne(curId);
                sum += curStaff.getSaleroom();
            }
            res = sum * 0.01;
        }
        System.out.println("add to " + this.getClass().getSimpleName() + " The bonus after decorating is:" + (preBonus + res));
        return preBonus + res;
    }
}

Finance finance class (the original class is not a decorator)

At this time, the financial class inherits the Rule, which means that the objects of the original class can be passed into other decorators as attributes. Is it more clear about the role of the Rule

package decorator.nopattern;

import decorator.withpattern.Rule;

import java.util.Map;

/**
 * @author xingyu
 * @date 2021/9/8 9:01
 * Finance((Finance)
 * Be responsible for calculating the salary of each employee
 * Primitive class (why is it called primitive class? It will be mentioned later)
 */
public class Finance extends Rule {

    /**
     * The basic bonus is 0
     * Later, if you want to add other calculation methods, just take this kind of object as the attribute of that decorator
     * Then, call the calculateBonus method of this object to calculate the previous bonus before formal calculation
     * It's a bit like a recursive execution process, layer by layer
     * @param id
     * @return
     */
    @Override
    public double calculateBonus(Long id) {
        double firstBonus = 0;
        System.out.println("The original bonus is:" + firstBonus);
        return firstBonus;
    }
}

Main method test

package decorator;

import decorator.nopattern.Finance;
import decorator.withpattern.Decorator;
import decorator.withpattern.DecoratorOne;
import decorator.withpattern.DecoratorThree;
import decorator.withpattern.DecoratorTwo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;

/**
 * @author xingyu
 * @date 2021/9/7 20:34
 * Decorator Pattern allows you to add new functionality to an existing object without changing its structure.
 * This type of design pattern belongs to structural pattern. It is a package of existing classes, and can realize the mixing of different decorative functions on the basis of the original functions.
 *
 * This pattern creates a decoration class to wrap the original class, and provides additional functions on the premise of maintaining the integrity of class method signature.
 * We use the following example to demonstrate the use of decorator pattern. For example, we will decorate a shape with different colors without changing the shape class.
 * For another example, grab cake is based on cake, and then added with decoration (such as vegetable leaves, sausage, meat slices)
 * Then the above-mentioned decorative mixing comes. I can realize a cake + vegetable leaves, a cake plus sausage, or a cake + sausage + meat slice
 * If you still have an impression of the arrangement and combination of high school, this decorative mix is the case
 * However, the result of each arrangement and combination must contain at least one original basic "cake"
 *
 * Case:
 *      The finance department pays the employees of the sales department (base salary 5000), and the employee bonus is divided into three categories
 *      1.Default bonus (regardless of sales): sales * 5%
 *      2.For employees with sales of more than 7000, the bonus is: 2% on the basis of the first
 *      3.The bonus of the sales manager is: the bonus of himself as an employee + the sales of all personnel of the sales department (including himself) * 1%
 */
@SuppressWarnings("all")
public class DecoratorTest {

    public static void main(String[] args) {

        //The original class object is the basis and necessary, and other decorations are decorated on this basis
        Finance finance = new Finance();
        //The first case is out of order (first calculate the amount of the second type bonus of employees, and then calculate the sum of the first type bonus and the second type bonus
        //Finally, what is the total bonus if the employee may be the manager)
//        Decorator decoratorTwo = new DecoratorTwo(finance);
//        Decorator decoratorOne = new DecoratorOne(decoratorTwo);
//        Decorator decoratorThree = new DecoratorThree(decoratorOne);
        //The second case is the normal sequence
//        Decorator decoratorOne = new DecoratorOne(finance);
//        Decorator decoratorTwo = new DecoratorTwo(decoratorOne);
//        Decorator decoratorThree = new DecoratorThree(decoratorTwo);
        //In the third case, delete a calculation method (assuming that the company cancels the first type of bonus)
        Decorator decoratorTwo = new DecoratorTwo(finance);
        Decorator decoratorThree = new DecoratorThree(decoratorTwo);
        decoratorThree.calculateBonus(3L);

        //Extension: decorator mode is used for I/O in Java. Are you familiar with this form
//        File file = new File("");
//        FileInputStream fis = new FileInputStream(file);
//        BufferedInputStream bis = new BufferedInputStream(fis);
    }
}

Display of execution results

Finisher mode summary:

Intent: dynamically add some additional responsibilities to an object. In terms of adding functionality, the decorator pattern is more flexible than generating subclasses.

Main solution: in general, we often use inheritance to extend a class. Because inheritance introduces static features into the class, and with the increase of extension functions, subclasses will expand.

When to use: extend the functionality of a class without adding many subclasses.

How to solve: divide the specific functions and responsibilities, and use the decorator mode at the same time.

Advantages: the decorated class and the decorated class can develop independently and will not be coupled with each other. The decoration mode is an alternative mode of inheritance. The decoration mode can dynamically expand the functions of an implementation class.

Disadvantages: multi-layer decoration is more complex.

Usage scenario: 1. Extend the function of a class. 2. Dynamic addition function and dynamic cancellation function.

Note: instead of inheritance.

I think the article is good. I'm looking for three links with one button online

After reading, did the friends have a deeper understanding of the decorator mode? I hope this article can help my friends better understand the design idea of decorator mode

After that, we began to slowly update each design mode, and take you to feel the world of design mode through vivid life phenomena. In fact, design mode is not difficult, but when we face a scene, we can't think of which design mode to use, whether to use design mode, and how to use it more reasonable
The blog content comes from Tencent classroom Duyi education tuoge, as well as some of his own understanding. At the same time, he has read the design pattern technical articles written by other Daniel

Posted by andr923 on Tue, 23 Nov 2021 17:39:27 -0800