Decorator Design Pattern

The Decorator pattern is a structural design pattern that allows you to attach additional behaviors to objects dynamically at runtime. This pattern is useful when you need to add new functionality to an object without modifying its source code. Instead, you can create new decorator objects that wrap the original object and add new behaviors to it. This pattern is often used in user interfaces, where different decorators can be used to modify the appearance or behavior of controls.

In this article, we will discuss the Decorator pattern in detail and explore its benefits, implementation, and use cases.

How the Decorator Pattern Works

The Decorator pattern involves two main components: the Component and the Decorator. The Component represents the original object that you want to decorate, while the Decorator is a wrapper that adds new behavior to the Component.

The Component is usually an interface or abstract class that defines the basic functionality of the object. The Decorator implements the same interface or abstract class as the Component, so it can be used in place of the original object. However, the Decorator adds new functionality by calling the methods of the Component and then adding its own behavior before or after the call.

Here is an example of how the Decorator pattern works in Java:

// Component interface
public interface Car {
void assemble();
}

// Concrete component
public class BasicCar implements Car {
@Override
public void assemble() {
System.out.println("Assembling basic car.");
}
}

// Decorator abstract class
public abstract class CarDecorator implements Car {
protected Car car;
public CarDecorator(Car car) {
    this.car = car;
}

public void assemble() {
    car.assemble();
}
}

// Concrete decorator
public class SportsCar extends CarDecorator {
public SportsCar(Car car) {
super(car);
}
public void assemble() {
    car.assemble();
    System.out.println("Adding features of Sports Car.");
}
}
// Client code
Car basicCar = new BasicCar();
basicCar.assemble(); // Assembling basic car.

Car sportsCar = new SportsCar(new BasicCar());
sportsCar.assemble(); // Assembling basic car. Adding features of Sports Car.


In this example, the Car interface represents the basic functionality of a car. The BasicCar class is a concrete implementation of the Car interface. The CarDecorator abstract class implements the same Car interface and has a reference to the Car component that it decorates. The SportsCar class is a concrete decorator that extends CarDecorator and adds new features to the basic car.

The client code can use the BasicCar and SportsCar objects interchangeably, and the SportsCar object can be used to decorate any other object that implements the Car interface.

Benefits of the Decorator Pattern


The Decorator pattern has several benefits, including:

Open-closed principle: The Decorator pattern follows the open-closed principle, which states that software entities should be open for extension but closed for modification. With the Decorator pattern, you can add new behaviors to an object without modifying its source code, making it easier to maintain and extend the codebase.

Single responsibility principle: The Decorator pattern promotes the single responsibility principle, which states that a class should have only one reason to change. With the Decorator pattern, you can separate the concerns of different behaviors into separate decorators, making it easier to modify and test the code.

Flexible and dynamic: The Decorator pattern allows you to add new behaviors to an object at runtime, making it more flexible and dynamic than other design patterns that require modification of the source code.

Practical Uses of Decorator Pattern

The Decorator pattern can be applied in a variety of real-world situations where you need to add new behaviors to an object dynamically without modifying its source code. Here are some examples:

  1. User Interfaces: In user interfaces, decorators can be used to modify the appearance or behavior of controls. For example, a text box control could be decorated with a label or an icon to provide additional information.
  2. Logging: In logging, decorators can be used to add additional information to log messages, such as timestamps or user information.
  3. Encryption: In encryption, decorators can be used to add additional layers of encryption to data. For example, a file could be encrypted using a base encryption algorithm and then decorated with a higher-level encryption algorithm.
  4. Caching: In caching, decorators can be used to add additional functionality to a cache, such as eviction policies or data expiration.
  5. Validation: In validation, decorators can be used to add additional validation rules to data, such as data type checking or length validation.

In all of these situations, the Decorator pattern provides a flexible and dynamic way to add new behaviors to an object at runtime without modifying its source code. This makes the code easier to maintain and extend, and promotes the principles of object-oriented design.