Introduction

The decorator design pattern is a structural pattern, that allow a user to add whatever features are required, dynamically, safely and in the easiest possible way,  without making any changes to the original code.

If we refer to SOLID principles, more precisely open-closed responsibility principle, classes should be open for extension but closed for modification. In other words, we should be able to add new behavior to existing classes but we will prevent any modification of existing code. This is exactly what decorator pattern does, so let’s take a look at pattern UML class  diagram below:"

UML class diagram

As you can notice the diagram has:

Component: its an interface or abstract class that describers behavior of concrete component as well as decorator.

ConcreteComponent: The actual object in which the new functionalities can be added dynamically.

Decorator: define all functionalities that can be added dynamically to the Concrete Component.

ConcreteDecorator1,2: decorator classes which hold the logic to decorate a given Component.

Example Implementation

Let's see an example implementing decorator pattern:

// Component
public interface Pizza {

	public String bakePizza();

}

// Concrete Component
public class BasicPizza implements Pizza {

	@Override
	public String bakePizza() {
		return "Basic Pizza";
	}

}

In the above example the Pizza class acts as the Component and BasicPizza is the concrete component which needs to be decorated.

Now as we have our pizza ,we'll  like to decorate it 😋

// Decorator
public abstract class PizzaDecorator implements Pizza {

	Pizza pizza;

	public PizzaDecorator(Pizza newPizza) {
		this.pizza = newPizza;
	}

	@Override
	public String bakePizza() {
		return pizza.bakePizza();
	}
}

The PizzaDecorator acts as a Decorator abstract class which contains a reference to the Pizza  class.

Finally, we can create as many custom decorators as we want.

lets create a few pizza decorators:

// ConcreteDecorator 1
public class ChickenPizza extends PizzaDecorator {

	public ChickenPizza(Pizza newPizza) {
		super(newPizza);
	}

	public String bakePizza() {
		return pizza.bakePizza() + " with Chicken topping added";
	}

}
// ConcreteDecorator 2
public class MincedMeatPizza extends PizzaDecorator {

	public MincedMeatPizza(Pizza newPizza) {
		super(newPizza);
	}

	public String bakePizza() {
		return pizza.bakePizza() + " with meat topping added";
	}
}

Client.java

public class Main {
	public static void main(String[] args) {
    
        //  decorating with ChickenPizza decorator
		Pizza pizza1 = new ChickenPizza(new BasicPizza());
        System.out.println(pizza1.bakePizza());
        
        // decorating with MincedMeatPizza decorator
        Pizza pizza2 = new MincedMeatPizza(new BasicPizza());
		System.out.println(pizza2.bakePizza());

	}
}

As we can see, we are now able to easily wrap the BasicPizza item in the way we want just by chaining the decorators.

Now your pizza is ready as you requested 😂 have a good meal.

Conclution:

In this article i attempted to demonstrate the benefits of using decorator pattern, and how it makes your code open for extension but closed for modification.

In Java, java.io package's most of the classes are implemented using this pattern,Once you understand this pattern very well, you will find it easy to understand package java.io in depth.

you can find more information about decorator in java.io in this article: https://stackoverflow.com/questions/6366385/use-cases-and-examples-of-gof-decorator-pattern-for-io

Hope you enjoyed this article and found it useful 😍.