Decorator Pattern

In software engineering, the decorator design pattern is used to add additional features or behaviors to a particular instance of a class without modifying the other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality …

In software engineering, the decorator design pattern is used to add additional features or behaviors to a particular instance of a class without modifying the other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.

Decorators provide a flexible alternative to sub-classing for extending functionality. Please note that the description above implies that decorating an object changes its behavior but not its interface.

This pattern is very important to understand because once you know the techniques of decorating, you’ll be able to give your (or someone else’s) objects new responsibilities without making any code changes to the underlying classes. Interesting, isn’t it??

1. Design Participants

A typical diagram of decorator patterns looks like this.

decorator design pattern participants
Decorator design pattern participants

Following are the participants of the Decorator Design pattern:

  • Component: this is the wrapper which can have additional responsibilities associated with it at runtime.
  • Concrete component: is the original object to which the additional responsibilities are added in the program.
  • Decorator: this abstract class contains a reference to the component object and implements the component interface.
  • Concrete decorator: they extend the decorator and build additional functionality on top of the Component class.

If you closely read between the lines, then you will understand that it works like this:

You have an instance, and you put another instance inside of it. They both support the same (or similar) interfaces. The one on the outside is a “decorator.” You use the one on the outside. It either masks, changes, or pass-troughs the methods of the instance inside of it.

I always prefer to learn things by example. So, let’s have one problem and solve it.

2. Problem statement

Let’s consider a common use case: We need to show the admin all user-created reports. These reports can fall into these categories.

  • Client reports
  • Support reports

Both of these reports must have a link to the original report in the first column, and they should have different coloring. Possibly, they should be opened in different popup window sizes. These are just a few things; a lot can come in reality.

A possible approach is to extend the Report object and then create two separate classes, ClientReport and SupportReport. In both methods, define private methods, e.g., makeAnchorLink(), designPopup(), applyColor(), and so on. Now, call these methods in some getter method for the first column data cell.

SubClassing

It will work if you stop here and do not modify the system anymore. But, let’s say after a few days, you are told to apply different background colors for both kinds of reports. Now you have only one way that defines a method colorBackground() in both classes and calls appropriately. The problem becomes worse when you have more reports in your system in the future.

The above solution clearly violates the Open/Closed principle mentioned in SOLID principles for class design.

3. Solution using Decorator Pattern

The above problem is a perfect candidate for decorator patterns. Remember when we have an object that requires the extension but by design that is not suitable, go for decoration.

The above problem can be solved easily by following the class diagram. Here, I am using it only for Support reports. A similar class hierarchy can be built for client reports as well.

Decorator Pattern Solution
Decorator Pattern Solution

If we implement our solution like this, we can add additional decoration without modifying the existing class hierarchy anytime in the future. That is the ultimate goal we had at the start of this post, right?

4. Implementation

Let’s see the above class’s source code to know what it actually looks like.

Report.java

public interface Report {

  public Object[][] getReportData(final String reportId);
  public String getFirstColumnData();
}

SupportReport.java

public class SupportReport implements Report {
 
  @Override
  public Object[][] getReportData(String reportId) {
    return null;
  }
 
  @Override
  public String getFirstColumnData() {
    return "Support data";
  }
}

ColumDecorator.java

public abstract class ColumDecorator implements Report {

  private Report decoratedReport;
   
  public ColumDecorator(Report report){
    this.decoratedReport = report;
  }
   
  public String getFirstColumnData() {
        return decoratedReport.getFirstColumnData(); 
    }
   
  @Override
  public Object[][] getReportData(String reportId) {
    return decoratedReport.getReportData(reportId);
  }
}

SupportLinkDecorator.java

public class SupportLinkDecorator extends ColumDecorator {

	public SupportLinkDecorator(Report report) {
		super(report);
	}

	public String getFirstColumnData() {
		return addMoreInfo (super.getFirstColumnData()) ;
	}

	private String addMoreInfo(String data){
		return data  + " - support link - ";
	}
}

SupportPopupDecorator.java

public class SupportPopupDecorator extends ColumDecorator{

	public SupportPopupDecorator(Report report) {
		super(report);
	}

	public String getFirstColumnData() {
		return addPopup (super.getFirstColumnData()) ;
	}

	private String addPopup(String data){
		return data  + " - support popup - ";
	}
}

5. Demo

Let’s write a simple test code to see how the decorator is used.

ClientPopupDecorator popupDecoratored 
		= new ClientPopupDecorator(new ClientLinkDecorator(new ClientReport()));
System.out.println(popupDecoratored.getFirstColumnData());
 
SupportPopupDecorator supportPopupDecoratored 
		= new SupportPopupDecorator(new SupportLinkDecorator(new SupportReport()));
System.out.println(supportPopupDecoratored.getFirstColumnData());

6. FAQs

6.1. How do you decide when to use the decorator pattern?

If we drill down more on the concept, we will find that the decorator design pattern has several requirement indicators to suggest that it is a potential solution, e.g.,

  • We have an object that requires the extension, e.g., a window control that requires additional “optional” features like scrollbars, title bars, and status bars.
  • Several objects support the extension by “decoration.” Usually, those objects share a common interface, traits, or superclass, and sometimes, additional, intermediate superclasses.
  • The decorated object (class or prototype instantiation) and the decorator objects have one or several common features. To ensure functionality, the decorated object and the decorators have a common interface, traits, or class inheritance.

6.2. Difference between Decorator pattern and Adapter pattern

These are not the same. Adapter pattern is used to convert the interface of an object into something else. Decorator pattern is used to extend the functionality of an object while maintaining its interface.

Both are probably sometimes called wrapper Patterns since they “wrap” an object.

6.3. Difference between a Decorator Pattern and Sub-classing

The difference between a Decorator pattern and subclassing is that you can decorate any class that implements an interface “with a single class”.

Say I wanted to give myself a java.util.Map that printed a message whenever I added or removed a key. If I only ever actually used java.util.HashMap I could just create PrintingMap? as a subclass of HashMap and override put & remove. But if I want to create a printing version of TreeMap then I either create PrintingTreeMap? (which has almost identical code to PrintingMap?

7. Decorator Pattern Examples in JDK

1) Java iO library classes e.g. BufferedInputStream bs = new BufferedInputStream(new FileInputStream(new File(“File1.txt”)));

2) In decorator column data in display-tag jsp library e.g.

<display:table name="reportsViewResultTable" class="demoClass" id="reportsQueryViewResultTable">
  <display:column title="Report Id" sortable="true" property="reportDisplayId" decorator="com.comp.FirstColumnDataDecorator"></display:column>
</display:table>

3) Decorators are used in site mesh to give a consistent UI experience.

Happy Learning !!

Weekly Newsletter

Stay Up-to-Date with Our Weekly Updates. Right into Your Inbox.

Comments

Subscribe
Notify of
7 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.