Decorator Design Pattern Example in Java [Tutorial]

The Decorator design pattern is one of the famous Gang of Four (GOF) structural design patterns, which provides a dynamic way of extending an object's functionality. It's different than the traditional way of adding new functionality into an object using Inheritance, instead, it uses Composition which makes it flexible and allows the addition of new functionalities at the run time, as opposite to Inheritance, which adds new functionality at compile time. Because of this flexibility, Decorator is one of the darling patterns for many Java developers. 

Like many other object-oriented design patterns, the decorator design pattern is introduced by the famous Gang of Four design pattern book, almost 2 decades ago. This means it's a time-tested way of adding new functionalities into an object.

In this Java design pattern tutorial, we will learn the Decorator design pattern by using it in a Java example. This is the best way of learning design patterns, followed you try it yourself to apply in similar scenarios. 

The decorator pattern is one of the popular design patterns along with the Factory method pattern and Singleton Pattern, and you can see its usage even in JDK itself. A couple of classes from the java.io package like BufferedInputStream, LineNumberInputStream are good examples of a Decorator design pattern in Java. 

Btw, if you are new to the design pattern world, then I also suggest you check The Java Design Patterns Masterclass which not only covers Decorator pattern but also other GOF patterns. It was also recently been updated to cover Java SE 8 implementation as well.




How to Code Decorator Design Pattern in Java

In order to show you, how to implement a Decorator pattern, let me first explain the requirements. Do we need to create software for calculating the price for a Sandwich, yummy... no? Since the customer can customize a sandwich by asking for extra cheese or extra fillings, you also need to include the cost of those items in the final price of the Sandwich. 

Since this customization can vary a lot among different customers and offer from a shop, creating classes for different types of Sandwich with different fillings or extras like BrownBreadSandWithCheese or WhiteBreaSandwitchWithCheeseAndTomato will just clutter the code with lots of endless small classes. 

Now this problem looks like a natural fit for applying the Decorator pattern because we have a base object Sandwich, which can be decorated with extra cheese and fillings. By using the Decorator pattern, you can extend the functionality of the Sandwich class at runtime, based upon the customer's request, which is impossible with Inheritance until you have a specific class for every possible customer request. 

This is also one of the reasons why Composition is preferred over Inheritance in Object-oriented design and particularly in Java. 

Now, let's see our class structure, We have an abstract class Sandwich, with abstract method price() and a concrete implementation class WhiteBreadSandwich, which cost $3.0

Now, in order to provide extra cheese, which obviously incurs an extra cost, we are going to use the Decorator design pattern. We have a Decorator abstract class, which will act as a base for Decorators called SandwichDecorator, and a concrete implementation of this as CheeseDecorator.

SandwichDecorator extends Sandwich, to be of the same type as the original object, which is getting decorated. This is a critical requirement of the Decorator pattern so that a decorated object can stand in place of the original object like it can be passed when a method expects the original object. 

Decorator adds functionality before or after delegating the task to the original object, which means in this example price of a WhilteBreadSandwich with Cheese will be calculated by the first calculating price of WhiteBreadSandwich and then the price of Cheese. 

Finally, I have a class called SandwichMaker, which will make a delicious sandwich for you :) I mean it will test the whole program and demonstrate how the Decorator pattern adds new functionality on the fly at runtime. 

I have also used BigDecimal to represent money instead of double primitive to follow best practices suggested in Java Programming Masterclass for Software Developers course by Tim BuchalakaUdemy. If you don't know why to read this Java mistake about using double to represent the price

decorator design pattern example in Java





UML diagram of Decorator Pattern

Here is the UML class diagram of the decorator design pattern. You can see that we have a Component interface, which is used to create ConcreteComponents. In our real-world example, Sandwich is a component interface and WhiteBreadSandwich is a concrete component. 

Now if we want to add an additional feature to the Component interface, we create another interface called Decorator, which inherits the Component interface, this is very important because it allows you to pass an instance of Decorator to any method which accepts an instance of the Component interface. 

Now you can create an implementation of a decorator, which has both functionalities provided by the original interface and new features provided by the decorator. This way you can add new features to the existing class hierarchy without modifying tried and tested code. 

This is another reason programmers say why the composition is better than Inheritance, but if you want to learn more about design principles and patterns, please see SOLID Principles of Object-Oriented Design, one of the best courses for a programmer who wants to write better code.

Anyway, here is the UML diagram of the Decorator pattern which we have talked about:


Real life example of Decorator design pattern in Java




Sample Code of Decorator Design Pattern in Java

Here is a complete Java program to demonstrate how you can implement a decorator pattern in Java. You can use this sample code to add more features and create new classes. If you are using Eclipse IDE, just create a Java project, select that project in the package explorer and copy the code there, it will automatically create the right packages and Java classes. 


Sandwich.java
import java.math.BigDecimal;

/**
 * Base class for all types of Sandwich, cost method is abstract because
 * different sandwiches has different price.
 *
 * @author Javin Paul
 */
public abstract class Sandwich {   
    protected String description = "Sandwich";  
   
    public String getDescription(){
        return description;
    }
   
    public abstract BigDecimal price();
}


WhiteBreadSandWich.java
import java.math.BigDecimal;

/**
 * A Concrete implementation of abstract Sandwich class, which represent a WhiteBread
 * Sandwich, whose price is 3.0$.
 *
 * @author Javin Paul
 */
public class WhiteBreadSandWich extends Sandwich {
   
    public WhiteBreadSandWich(String desc){
       description = desc;
    }
   
    @Override
    public BigDecimal price() {
        return new BigDecimal("3.0");
    }
   
}

SandWichDecorator.java
/**
 * Base class for Decorators, this class inherit from Sandwich, so that
 * it can be of same type, which is required to pass decorators where
 * original object is expected. Later, this class will also come handy
 * to provide common functionalities to Decorators.
 *
 * @author
 */
public abstract class SandWichDecorator extends Sandwich {  
   
    @Override
    public abstract BigDecimal price();
   
}

CheeseDecorator.java
import java.math.BigDecimal;

/**
 * A Decorator class, which adds cheese (new functionality) into Sandwich object.
 * This Decorator class modifies price() and getDescritption() method to implement
 * new behaviour.
 *
 * @author
 */
public class CheeseDecorator extends SandWichDecorator{
    Sandwich currentSandwich;
   
    public CheeseDecorator(Sandwich sw){
        currentSandwich = sw;
    }
   
    @Override
    public String getDescription(){
        return currentSandwich.getDescription() + ", Cheese";
    }
    @Override
    public BigDecimal price() {
        return currentSandwich.price().add(new BigDecimal("0.50"));
    }
   
}


SandwichMaker.java
/**
 * Test class to demonstrate How Decorator Pattern in Java work together. This class
 * first creates a Sandwich and decorates it with extra cheese. This is nice example
 * of how to provide new functionalities to an object at runtime using Decorator Pattern.
 *
 * @author Javain Paul
 */
public class SandwichMaker {
   
    public static void main(String args[]){
       
        Sandwich mySandwich = new WhiteBreadSandWich("White bread Sandwich");
        System.out.printf("Price of %s is $%.2f %n", mySandwich.getDescription(), 
                                                     mySandwich.price());
       
        //adding extra cheese using Decorator Pattter
        mySandwich = new CheeseDecorator(mySandwich);
        System.out.printf("Price of %s is $%.2f %n", mySandwich.getDescription(), 
                                                     mySandwich.price());
    }
}

Output:
Price of White bread Sandwich is $3.00
Price of White bread Sandwich, Cheese is $3.50

You can see that now price is easily calculated based upon the toppings and extras you add to the Sandwich. 

These design patterns are very useful for writing code that can withstand the test of time and that's why every serious programmer should learn patterns. If you want to learn more about other OOP design patterns like Decorator and their modern implementation in Java then you can take a look at the Design Patterns in Java course by Dmitri Nestruck on Udemy.





6 Things about Decorator Design Pattern You Should Know

Now, we have seen an example of a decorator pattern in Java, we can quickly summarize few important things which are worth remembering while implementing or applying decorator pattern or even to answers design pattern questions like When to use Decorator design pattern in Java.

1. The Decorator must be of the same type of object, which they are decorating. This can be achieved either by implementing the interface of the object or by extending an abstract class of the original class.

2. The decorator pattern is based on Composition, which means it needs an original object to decorate it. This is achieved by creating a constructor on the decorator class that accepts a base type of original object. like in this example constructor of CheeseDecorator accepts Sandwich object. 

3. Decorator class adds new functionality before or after delegating the task to the original object. In this example, the price of the Decorator i.e. cheese is included after calculating the price of the White Bread Sandwich.

4. The decorator pattern is also a good example of the Open-Closed design principle, which is one of the key principles from Uncle Bob's SOLID design principles. 

5. Remember, the Decorator design pattern only affects objects at runtime, it doesn't affect the class. You should use the Decorator Pattern when your intent is to add new functionality at runtime (i.e. a customer order, where you only know about order details, one is placed).

6. There is one disadvantage of the Decorator pattern as well, it adds lots of small classes in the code base, remember the overwhelming number of classes in the java.io package. Though, once you know which classes are main classes, and which are decorators, you tend to get a better understanding of the overall structure. UML diagrams certainly help in this case.

That's all on Decorator design pattern in Java and Object-oriented design. I must say, this is one of the must-know design patterns for senior Java developers, it's general purpose and has lots of use cases as well.

Further Learning
Design Pattern Library
Java Design Patterns - The Complete Masterclass
Software Design Patterns: Best Practices for Software Developers


Other Java Design Patterns tutorials you may like
  • 5 Free Courses to learn Object Oriented Programming (courses)
  • Difference between Factory and Dependency Injection Pattern? (answer)
  • How to create thread-safe Singleton in Java? (example)
  • How to implement the Strategy Design Pattern in Java? (example)
  • Difference between Factory and AbstractFactory Pattern? (example)
  • 18 Java Design Pattern Interview Questions with Answers (list)
  • Difference between Factory and Abstract Factory pattern in Java (answer)
  • How to design a Vending Machine in Java? (questions)
  • 20 System Design Interview Questions (list)
  • How to get a method of HashMap to work internally in Java? (answer)
    Difference between Abstraction and Encapsulation in Java? (answer)
  • Difference between State and Strategy Design Pattern in Java? (answer)
  • Top 5 Courses to learn Design Patterns in Java (courses)
  • 5 Free Courses to learn Data Structure and Algorithms (courses)
Thanks a lot for reading this article so far. If you like this Decorator design pattern tutorial in Java then please share it with your friends and colleagues. If you have any questions or feedback then please drop a note.

P. S. - If you want to learn more about design patterns and how they can help you to write better code in Java, I suggest you take a look at the Java Design Patterns and Architecture - a Free course on Udemy.

7 comments:

  1. Decorator Patter is true example of Composition over Inheritance. I used it a lot in my code, until I realized that, lots of small classes. Do you know any pattern or Strategy to merge Decorators together to gain benefit of this pattern and negate number of small classes?

    ReplyDelete
    Replies
    1. AbstractFacotry pattern may be a good at aid

      Delete
  2. Dude Try Some thing like
    A Sandwich Class.
    Toppings Abstract class with price as methods. And have multiple toppings like Cheese, Grilled, Sauces etc
    Let Sandwich class has Array of Toppings[]
    In Sandwich price add base price and Iterate over Toppings array to get price and Add them to sandwich.

    ReplyDelete
  3. for the beginner who start to learn design pattern this is a great place to learn design patterns with real world example.

    ReplyDelete
  4. Thanks Javin, and Thanks Ashish too. I was thinking how to do multiple customization.

    ReplyDelete
  5. And in which exactly real life situation will you have to recompile each time you change a price or add a new filling?

    ReplyDelete
  6. please tell real example of decorator?

    ReplyDelete

Feel free to comment, ask questions if you have any doubt.