This is the 13th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

preface

In general, if we want to extend an object, we do it by inheritance or composition. The action of the extension happens at compile time. For example, when we inherit an extension, all the objects of the subclass are determined at compile time.

If you need to extend functionality at run time for different situations without adding or removing functionality from existing objects, you need to use the decorator pattern.

Decorator pattern definition

The Decorator Design Pattern is used to modify the functionality of an object at run time. At the same time, other instances of the same class will not be affected by this, so a single object will get the modified behavior. Decorator design pattern is a type of structural design pattern (such as adapter pattern, bridge pattern, composition pattern) that is implemented using abstract classes or interfaces with composition.

Suppose we wanted to implement different types of cars, we could create an interface Car to define what functions the Car has (methods in the interface), then we could have a base Car implementation class, and further we could extend base to sports cars and luxury cars. The implementation hierarchy is shown below.

However, the implementation becomes complicated if we want to have a car with both sports car and luxury car characteristics at run time, and it becomes even more complicated if we want to further specify which feature should be added first. Now imagine if we had 10 different cars, the implementation logic using inheritance and composition would be particularly unmanageable. To solve this programming problem, we applied the decorator pattern in Java.

The following types of components are generally required to implement the decorator pattern.

Component interface

The interface or abstract class that defines the method to be implemented. In our example, Car will be the component interface.

public interface Car {

	public void assemble(a);
}
Copy the code

Component implementation

The basic implementation of a component interface. We can implement the BasicCar class as a component.

public class BasicCar implements Car {
	@Override
	public void assemble(a) {
		System.out.print("Basic car."); }}Copy the code

A decorator

The Decorator class implements the component interface and has a HAS-A relationship with it. The component variable should be accessible to the quilt decorator class, so we make it protected.

public class CarDecorator implements Car {

    protected Car car;

    public CarDecorator(Car c){
        this.car=c;
    }
    
    @Override
    public void assemble(a) {
        this.car.assemble(); }}Copy the code

Extend basic decorator functionality and modify component behavior accordingly. We can have specific decorator classes such as LuxuryCar and SportsCar.

Sports car decorator implementation:

public class SportsCar extends CarDecorator {

    public SportsCar(Car c) {
        super(c);
    }

    @Override
    public void assemble(a){
        super.assemble();
        System.out.print("Add sports car features"); }}Copy the code

Luxury car decorator implementation:

public class LuxuryCar extends CarDecorator {

	public LuxuryCar(Car c) {
		super(c);
	}
	
	@Override
	public void assemble(a){
		super.assemble();
		System.out.print("Add luxury car features"); }}Copy the code

The test class:

public class DecoratorPatternTest {

	public static void main(String[] args) {
		Car sportsCar = new SportsCar(new BasicCar());
		sportsCar.assemble();
		// The target object BasicCar can be flexibly extended (decorated)
		Car sportsLuxuryCar = new SportsCar(new LuxuryCar(newBasicCar())); sportsLuxuryCar.assemble(); }}Copy the code

In our test code, we had the flexibility to extend the target object BasicCar, and the order of SportCar and LuxuryCar could be changed.

Decorator pattern class diagram

summary

The decorator design pattern helps provide runtime modification capabilities and is therefore more flexible. When the number of choices is large, it is easy to maintain and expand.

Decorator mode is widely used in Java IO classes, such as FileReader and BufferedReader.

If it helps you, giving me a “like” would be a big boost.