Learning design pattern - Decorator Pattern

summary

Decorator Pattern allows adding new functions to an existing object without changing its structure. It is used as a wrapper for an existing class.

To extend the functionality of an object, the decorator pattern provides a more flexible alternative than inheritance. Combination is better than inheritance.

When to use:

  • You need to extend the functionality of a class or add additional responsibilities to a class.
  • You need to dynamically add functions to an object, and these functions can be dynamically revoked.
  • It is necessary to add a very large number of functions generated by the arrangement and combination of some basic functions, so that inheritance becomes unrealistic.

UML class diagram:

Role composition:

  1. Abstract component role: defines the interface of an object that can dynamically add tasks
  2. Concrete Component: defines an object to be decorated by the decorator, that is, the concrete implementation of the Component
  3. Abstract decorator: it holds an instance of a component object and defines an interface consistent with the abstract component. It maintains references to component objects and their subclasses
  4. Concrete decorator role: add new responsibilities to the component

General code

Abstract component role:

public interface Component {
    void operation();
}

Specific component roles:

public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        // Specific business code
        System.out.println("ConcreteComponent General basic logic");
    }
}

Abstract decorative role:

public abstract class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        // Delegate to component
        component.operation();
    }
}

Specific decorative roles:

public class ConcreteDecorationA extends Decorator {

    public ConcreteDecorationA(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        decorateMethod();
    }

    // Define your own modification logic
    private void decorateMethod() {
        System.out.println("ConcreteDecorationA Modified logic");
    }
}

public class ConcreteDecorationB extends Decorator {

    public ConcreteDecorationB(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        decorateMethod();
    }

    // Define your own modification logic
    private void decorateMethod() {
        System.out.println("ConcreteDecorationB Modified logic");
    }
}

use:

public class Test {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Decorator decoratorA = new ConcreteDecorationA(component);
        decoratorA.operation();
    }
}

result:

ConcreteComponent general basic logic
Modification logic of concrete decoration a

Seeing this, I believe you know a lot about the general code structure of decorator mode. Let's further explain it through a practical use case.

example

I believe everyone has drunk milk tea. There are many kinds of milk tea, such as coconut milk tea, QQ milk tea, chocolate milk tea, etc. different desserts such as coconut, red beans and pudding can be added to milk tea. Milk tea shops now sell milk tea of various flavors. If the decoration mode is not used, all kinds of different milk tea must produce one class in the sales system. If there are 5 kinds of milk tea and 5 kinds of desserts, at least 25 classes (excluding mixed flavors) will be generated. If the decoration mode is used, several classes can be completed.

Milk tea interface class

public interface MilkTea {
    // Return milk tea description
    String getDescription();
    // Return price
    double getPrice();
}

Specific milk tea categories: chocolate milk tea and QQ milk tea

public class ChocolateMT implements MilkTea{
    private String description = "Chocolate Milk Tea";
    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public double getPrice() {
        return 15;
    }
}


public class QQMT implements MilkTea {
    private String description = "QQ tea with milk";
    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public double getPrice() {
        return 10;
    }
}

Abstract decoration class

public abstract class Decorator implements MilkTea {

    protected MilkTea milkTea;

    public Decorator(MilkTea milkTea) {
        this.milkTea = milkTea;
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription();
    }

    @Override
    public double getPrice() {
        return milkTea.getPrice(); // The price depends on the type
    }
}

Specific decoration: add coconut to milk tea

public class Coconut extends Decorator {

    private String description = "With coconut!";

    public Coconut(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription() + "\n" + description;
    }

    @Override
    public double getPrice() {
        return milkTea.getPrice() + 3;  // 3 represents the price of coconut
    }

}

Specific decoration: add pudding to milk tea

public class Pudding extends Decorator {

    private String description = "With pudding!";

    public Pudding(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription() + "\n" + description;
    }

    @Override
    public double getPrice() {
        return milkTea.getPrice() + 5;  // 5 means the price of pudding
    }
}

Specific decoration: add pearls to milk tea

public class Pearl extends Decorator {

    private String description = "Add pearls!";

    public Pearl(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription() + "\n" + description;
    }

    @Override
    public double getPrice() {
        return milkTea.getPrice() + 10;  // 10 represents the price of pearls
    }
}

test

// The first way to write
public class Test {
    public static void main(String[] args) {
        // Choose chocolate milk tea
        MilkTea milkTea = new ChocolateMT();
        // For the first modification, add pudding to chocolate milk tea
        milkTea= new Pudding(milkTea);
        // The second modification is to add coconut to chocolate milk tea
        milkTea = new Coconut(milkTea);
        System.out.println(milkTea.getDescription() + "\n Price of chocolate milk tea with pudding:" + milkTea.getPrice());
    }
}

// The second way to write
public class Test {
    public static void main(String[] args) {
        // Choose chocolate milk tea
        MilkTea milkTea = new ChocolateMT();
        // For the first modification, add pudding to chocolate milk tea
        Pudding puddingMilkTea = new Pudding(milkTea);
        // The second modification is to add coconut to chocolate milk tea
        Coconut coconutMilkTea = new Coconut(puddingMilkTea);
        System.out.println(coconutMilkTea.getDescription() + "\n Price of chocolate milk tea with pudding:" + coconutMilkTea.getPrice());
    }
}

Test results:

Chocolate Milk Tea
With pudding!
With coconut!
Price of chocolate milk tea with pudding: 23.0

The results of the two ways of writing test cases are the same

summary

As can be seen from the above case, the code structure of the decorator pattern uses the combination of classes to replace inheritance, but in fact, compared with the simple combination relationship, the decorator pattern has two special places.

The first special point is that the decorator class inherits the same parent class as the original class, so that we can "nest" multiple decorator classes on the original class. For example, the second writing method in the above test class nested two decorator classes for chocolate milk tea, adding coconut and pudding to it.

The second special point is that the decorator class is an enhancement of functions, which is also an important feature of the application scenario of decorator mode. In fact, there are many design patterns that conform to the code structure of "composite relationship", such as agent pattern, bridge pattern and decorator pattern. Although their code structure is very similar, the intention of each design pattern is different. Take the more similar proxy mode and decorator mode. In the proxy mode, the proxy class adds functions unrelated to the original class, while in the decorator mode, the decorator class adds enhancements related to the original class.

The IO class library in Java is a classic scenario for using the decorator pattern.

InputStream in = new FileImputStream("/user/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();

Finally

I've always wanted to sort out a perfect interview dictionary, but I can't spare time. This set of more than 1000 interview questions is sorted out in combination with the interview questions of gold, silver and four major factories this year, as well as the documents with star number exceeding 30K + on GitHub. After I uploaded it, it's no surprise that the praise amount reached 13k in just half an hour. To be honest, it's still a little incredible.

You need a full version of the little partner. You can click three times, click here !

1000 Internet Java Engineer Interview Questions

Content: Java, MyBatis, ZooKeeper, Dubbo, Elasticsearch, Memcached, Redis, MySQL, Spring, SpringBoot, SpringCloud, RabbitMQ, Kafka, Linux and other technology stacks (485 pages)

Collection of Java core knowledge points (page 283)

The content covers: Java foundation, JVM, high concurrency, multithreading, distribution, design pattern, Spring bucket, Java, MyBatis, ZooKeeper, Dubbo, Elasticsearch, Memcached, MongoDB, Redis, MySQL, RabbitMQ, Kafka, Linux, Netty, Tomcat, database, cloud computing, etc

Collection of advanced core knowledge points in Java (page 524)

Sorting out knowledge points of Java advanced architecture

 

Due to space constraints, the detailed information is too comprehensive and there are too many details, so only the screenshots of some knowledge points are roughly introduced. There are more detailed contents in each small node!

You need a full version of the little partner. You can click three times, click here !

Keywords: Java Design Pattern

Added by lordphate on Tue, 21 Dec 2021 20:26:36 +0200