Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
Decorator Design Pattern
2.1.
Example
2.2.
Java
3.
The Decorator Model
4.
Implementation of Decorator Design Pattern 
4.1.
Output
5.
Need for Decorator Design Pattern
6.
Advantages
7.
Disadvantages 
8.
Frequently Asked Questions
8.1.
What is decorator in software engineering?
8.2.
What is decorator design pattern in Java 8?
8.3.
What is Decorator pattern in real life?
8.4.
What is the main purpose of a decorator pattern?
9.
Conclusion
Last Updated: Mar 27, 2024
Medium

Decorator Design Pattern

Author Muskan Sharma
0 upvote
Crack Google SDE interview : Essential projects
Speaker
Saurav Prateek
SDE-2 @
20 Jun, 2024 @ 01:30 PM

Introduction

The Decorator design pattern is generally used to change the functionality of an object at runtime. It works without affecting other objects in the same class, so individual objects get the modified behaviour.

So what to do now? Now, the decorator design pattern will do this task for you. The decorator design pattern is used to change an object's functionality during runtime.

Decorator Design Pattern


This article will help you understand how decorative design pattern work, the needs of decorative design pattern and the advantages and disadvantages of decorative design pattern.

Decorator Design Pattern

The decorator design pattern allows dynamic modifications to an object's functionality at runtime without affecting other instances of the same class. This ensures that the behavior of each object remains unaltered and independent of one another. The Decorator design pattern is a structural design pattern implemented using abstract classes or interfaces with composition.

Now, the question that comes to your mind will be, why use the decorator design pattern when we can do this with inheritance?

The decorator design pattern allows you to extend or decorate an object's functionality. It is a substitute for inheritance. Decorator adds functionality to a specific instance at run-time, whereas inheritance adds functionality at compilation time, and the change impacts all instances of the new class.

Moving forward, in the article's next section, you’ll learn about the decorator model.

Example

Let us understand decorator design pattern with the help of simple example:

  • Java

Java

// Component Interface
interface Coffee {
String getDescription();
double getCost();
}

// Concrete Component
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}

@Override
public double getCost() {
return 2.0;
}
}

// Decorator
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;

public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}

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

@Override
public double getCost() {
return decoratedCoffee.getCost();
}
}

// Concrete Decorators
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}

@Override
public String getDescription() {
return super.getDescription() + ", Milk";
}

@Override
public double getCost() {
return super.getCost() + 0.5;
}
}

class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}

@Override
public String getDescription() {
return super.getDescription() + ", Sugar";
}

@Override
public double getCost() {
return super.getCost() + 0.2;
}
}


class DecoratorPatternExample {
public static void main(String[] args) {
// Simple Coffee
Coffee coffee = new SimpleCoffee();
System.out.println("Cost: " + coffee.getCost() + ", Ingredients: " + coffee.getDescription());

// Coffee with Milk
Coffee milkCoffee = new MilkDecorator(coffee);
System.out.println("Cost: " + milkCoffee.getCost() + ", Ingredients: " + milkCoffee.getDescription());

// Coffee with Milk and Sugar
Coffee milkSugarCoffee = new SugarDecorator(milkCoffee);
System.out.println("Cost: " + milkSugarCoffee.getCost() + ", Ingredients: " + milkSugarCoffee.getDescription());
}
}

Explanation: In this example, Coffee is the component interface representing the base object. SimpleCoffee is the concrete component. CoffeeDecorator is the abstract decorator providing the common structure for concrete decorators like MilkDecorator and SugarDecorator. These concrete decorators enhance the functionality and modify the cost of the base coffee object. The client code demonstrates how decorators can be stacked to create a customized coffee with different ingredients and costs.

Output:

output
Get the tech career you deserve, faster!
Connect with our expert counsellors to understand how to hack your way to success
User rating 4.7/5
1:1 doubt support
95% placement record
Akash Pal
Senior Software Engineer
326% Hike After Job Bootcamp
Himanshu Gusain
Programmer Analyst
32 LPA After Job Bootcamp
After Job
Bootcamp

The Decorator Model

The Decorator class diagram notation depicts two fundamental elements: component and decorator.

The Decorator Model
  • Component: The component represents the thing that will be decorated.
     
  • Decorator: The decorator abstract class represents concrete decorations.
     
  • Concrete ComponentThe concrete component is what is decorated.
     
  • Concrete DecorationsThe actual decorations are concrete decorations. 
     

The source of all concrete decoration and the concrete component is the same package we are working on.

Now let us look at the implementation of the decorator design pattern.

Implementation of Decorator Design Pattern 

Let us look at the implementation of the decorator design pattern.
 

Step 1: Firstly, we have to create an interface.

public interface ChristmasTree {
	public String makeChristmasTree();
}


Step 2: Create concrete classes with the same interface as the abstract classes.

public class SimpleChristmasTree implements ChristmasTree {

	@Override
	public String makeChristmasTree() {
		return "Base ChristmasTree";
	}
}

 

Step 3: Create abstract decorator classes with the same interface as the concrete classes.

abstract class ChristmasTreeDecorator implements ChristmasTree {
	protected ChristmasTree specialChristmasTree;

	public ChristmasTreeDecorator(ChristmasTree specialChristmasTree) {
		this.specialChristmasTree = specialChristmasTree;
	}

	public String makeChristmasTree() {
		return specialChristmasTree.makeChristmasTree();
	}
}


Step 4: Create a concrete decorator class that extends the previously specified abstract decorator class.

public class BellsDecorator extends ChristmasTreeDecorator {

	public BellsDecorator(ChristmasTree specialChristmasTree) {
		super(specialChristmasTree);
	}

	public String makeChristmasTree() {
		return specialChristmasTree.makeChristmasTree() + addBells();
	}

	private String addBells() {
		return " + Ringing Bells";
	}
}

public class LightDecorator extends ChristmasTreeDecorator {

	public LightDecorator(ChristmasTree specialChristmasTree) {
		super(specialChristmasTree);
	}

	public String makeChristmasTree() {
		return specialChristmasTree.makeChristmasTree() + addLight();
	}

	private String addLight() {
		return " + Twinkling lights";
	}
}

public class GiftsDecorator extends ChristmasTreeDecorator {

	public GiftsDecorator(ChristmasTree specialChristmasTree) {
		super(specialChristmasTree);
	}

	public String makeChristmasTree() {
		return specialChristmasTree.makeChristmasTree() + addGifts();
	}

	private String addGifts() {
		return " + Gifts ";
	}
}


Step 5: You may now use the concrete decorator class you created to decorate interface objects.

public class TestDecorator {

	public static void main(String args[]) {
		ChristmasTree ChristmasTree = new GiftsDecorator(new LightDecorator(new BellsDecorator(new SimpleChristmasTree())));
		System.out.println(ChristmasTree.makeChristmasTree());
	}
}

Output

Output


Explanation: As in the output above, we are changing the behavior and functionality of base ChristmasTree by adding the Ringing Bells, Twinkling lights and gifts without affecting the other classes.

Need for Decorator Design Pattern

Let us understand the need for decorative design pattern with the help of an example.

Assume you're working on a website and want to change or modify certain aspects. It will be challenging to do this on a regular basis, much like working on a retail website where you must undertake regular advertising, such as weekly specials and the launch of new products. So you need to have the flexibility for web design or structure.

Now, you want to change or modify the components without altering the structure of the website. This is where the decorator design pattern comes into play. The Decorator pattern solves the issue of maintaining structure while allowing for modifications by decorating the various components that comprise the application.

As you can see, this design pattern allows you to mix and match all components and decorations for maximum flexibility and expandability while maintaining basic structure classes.

Now at the end of the article, let us look at the advantages and disadvantages of decorator design pattern.

Advantages

1. The Decorator design pattern offer an extra flexible alternative rather than creating a new subclass for adding functionality.

2. The Decorator design pattern effectively solves permutation problems since you can wrap a component in any number of decorators.

3. The Decorator design pattern allows behavior modification at runtime rather than going back to the existing code and making changes.

4. The Decorator design pattern helps in extending the hierarchy.

Disadvantages 

1. The Decorator design pattern can result in a large number of small components in our design, and overuse can be complicated.

2. The Decorator design pattern could cause problems if the client values the concrete components highly.

3. The Decorator design pattern must be compatible with the original object's interface and behavior. Otherwise, unexpected errors or inconsistencies could occur.

4. The Decorator design pattern can result in loss of information or functionality of the original object because it only exposes the common interface and hides specific details.

Frequently Asked Questions

What is decorator in software engineering?

In software engineering, a decorator is a structural design pattern that dynamically adds or alters the behavior of objects without changing their code.

What is decorator design pattern in Java 8?

In Java 8, the decorator design pattern allows you to dynamically attach additional responsibilities to objects. It's often used for extending functionalities in a flexible and reusable way.

What is Decorator pattern in real life?

In real life, the Decorator pattern is akin to adding layers to a cake. Each layer represents an additional feature, providing a way to customize and extend the cake's appearance and taste.

What is the main purpose of a decorator pattern?

The main purpose of a decorator pattern is to attach additional responsibilities to objects dynamically. It allows behavior to be added or removed at runtime, enhancing flexibility and avoiding class explosion.

Conclusion

In this article, we learned what decorator design pattern is. The decorator model, implementation of decorator design pattern, the need of decorator design pattern, and the advantages and disadvantages of decorator design pattern. We hope this article helped you in knowing more about the decorator design pattern.

If you want to learn more, refer to these articles: 

You can also consider our System Design Course to give your career an edge over others.

Happy Learning, Ninja!

Previous article
Delegation Event Model in Java
Next article
Factory Design Pattern in Java
Live masterclass