Skip to content

Latest commit

 

History

History
101 lines (79 loc) · 6.75 KB

2D. Decorator.md

File metadata and controls

101 lines (79 loc) · 6.75 KB

Decorator

The Decorator design pattern allows you to add new behavior or modify existing behavior of an object dynamically without changing its underlying implementation. It involves creating a set of decorator classes that wrap the original object and provide additional functionality.

image

// Component
interface Vehicle {
    void accelerate();
}

// Concrete Component
final class ConstructionVehicle implements Vehicle {
    @Override
    public void accelerate() {
        System.out.println("Construction vehicle is accelerating.");
    }
}

// Decorator
abstract class VehicleDecorator implements Vehicle {
    protected Vehicle decoratedVehicle;

    public VehicleDecorator(Vehicle decoratedVehicle) {
        this.decoratedVehicle = decoratedVehicle;
    }

    @Override
    public void accelerate() {
        decoratedVehicle.accelerate();
    }
}

// Concrete Decorator
class BulldozingDecorator extends VehicleDecorator {

    private String driver;

    public BulldozingDecorator(Vehicle decoratedVehicle, String driver) {
        super(decoratedVehicle);
        this.driver = driver;
    }

    @Override
    public void accelerate() {
        super.accelerate();
        System.out.println("Driver " + driver + " activated the bulldozer's Blade! pushing construction materials ...");
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle constructionVehicle = new ConstructionVehicle();
        constructionVehicle.accelerate();
        /* OUTPUT
        Construction vehicle is accelerating.
        */

        Vehicle tomBulldozer = new BulldozingDecorator(constructionVehicle, "Tom");
        tomBulldozer.accelerate();
        /* OUTPUT
        Construction vehicle is accelerating.
        Driver Tom activated the bulldozer's Blade! pushing construction materials ...
        */
    }
}

In this example, we have an interface called Vehicle with a single method accelerate(). The ConstructionVehicle class is a concrete implementation of the Vehicle interface. It represents a construction vehicle that can accelerate. The VehicleDecorator class is an abstract class that implements the Vehicle interface. It acts as the base class for decorators. It contains a reference to a Vehicle object that it decorates. The accelerate() method of VehicleDecorator invokes the accelerate() method of the decorated vehicle.

The BulldozingDecorator is a specific decorator that extends the VehicleDecorator class. It adds extra fields named driver and also adds the functionality of bulldozing to the decorated vehicle. When the accelerate() method of BulldozingDecorator is called, it first invokes the accelerate() method of the decorated vehicle and then adds the specific behavior of activating the blade and pushing construction materials.

In the Main class, we demonstrate how the decorators can be used. We start by creating a ConstructionVehicle object. Next, we create a bulldozer object by wrapping the constructionVehicle with the BulldozingDecorator. When we call the bulldozer.accelerate() method, it first invokes the accelerate() method of the decorated constructionVehicle, which outputs "Construction vehicle is accelerating." Then, it adds the behavior specific to the BulldozingDecorator, which outputs "Blade activated! pushing construction materials ..."

By using decorators, we can dynamically add new features or modify the behavior of an object at runtime without changing its underlying implementation. In this example, the BulldozingDecorator adds the bulldozing functionality to a construction vehicle without modifying the ConstructionVehicle class directly. This allows for flexibility and extensibility in adding new behaviors to objects.

Decorator Design Pattern Summary

Use the Decorator pattern when you need to be able to assign extra behaviors to objects at runtime without breaking the code that uses these objects.

  • The Decorator lets you structure your business logic into layers, create a decorator for each layer and compose objects with various combinations of this logic at runtime. The client code can treat all these objects in the same way, since they all follow a common interface.
    • The Vehicle interface acts as the common interface. The client interacts with the Vehicle objects, whether they are the base ConstructionVehicle or the decorated BulldozingDecorator.

Use the pattern when it’s awkward or not possible to extend an object’s behavior using inheritance.

  • Many programming languages have the final keyword that can be used to prevent further extension of a class. For a final class, the only way to reuse the existing behavior would be to wrap the class with your own wrapper, using the Decorator pattern.
    • ConstructionVehicle class is final, indicating that it cannot be directly subclassed any further to add new behaviour.
    • To be able to add new behaviour would require us to use the Decorator pattern to add the "bulldozing" behavior to the ConstructionVehicle.
      • The BulldozingDecorator acts as a wrapper around the ConstructionVehicle and provides the additional behavior of activating the blade. By using the Decorator pattern, we can extend the behavior of the ConstructionVehicle without modifying its implementation or inheriting from it directly, which would not be possible if it were marked as final.

NOTE

  • How does the Decorator pattern differs from the Proxy Pattern?
    • The Decorator pattern can be used to add additional attributes (fields) to an object. Moreover, It can be used with the Builder pattern to dynamically transform an object by setting additional optional attributes (fields) via the Director's contstruct methods, effectively creating very flexible object. Hence, It allows dynamically adding new behaviors or functionalities to an object by wrapping it with a decorator object. The decorator object can modify the behavior or extend the functionality of the original object without changing its underlying structure.
      • Wrapping the actual VehicleDecorator implementation with additional fields (driver)
    • On the other hand, the Proxy pattern focuses on calling the functions or methods of the underlying real class. It provides a surrogate or placeholder object that controls the access to the real object. The proxy object acts as an intermediary, allowing the client to interact with the real object indirectly. The proxy can add additional logic or perform certain operations before or after delegating the function calls to the real object.