Design Patterns Series — Decorator Design Pattern

Baran B.
5 min readApr 1, 2023

The Decorator Design Pattern is a powerful and flexible structural design pattern that allows you to extend the functionality of an object dynamically without modifying its original structure. It’s particularly useful when you need to add or modify behaviors of an object without affecting other instances of the same class. In this article, we will explore what the Decorator pattern is, when and where to use it, its advantages and disadvantages, and a real-world examples using the JDK’s InputStream and GUI Components.

What is the Decorator Design Pattern?

The Decorator pattern involves a set of decorator classes that are used to wrap concrete components. These decorators have the same interface as the components they are decorating, allowing them to be used interchangeably, but they can add or override functionality as needed.

The key elements of the Decorator pattern are:

  1. Component: An abstract class or interface that defines the common interface for all concrete components and decorators.
  2. Concrete Component: A class that implements the Component interface, providing a basic implementation.
  3. Decorator: An abstract class that extends the Component, serving as the base for all concrete decorators.
  4. Concrete Decorator: A class that extends the Decorator, implementing specific functionality or modifying existing behavior.

When and Where Should You Use the Decorator Pattern?

You should consider using the Decorator pattern when:

  1. You need to add or modify functionality of an object at runtime without changing its original structure.
  2. Inheritance is not a viable solution due to the complexity or inflexibility it introduces.
  3. You want to keep your code modular and maintainable by separating concerns and avoiding monolithic classes.

Some common use cases for the Decorator pattern include:

  • Graphical User Interfaces (GUIs): Decorators can be used to dynamically add or modify visual elements or behaviors of UI components.
  • Input/output (I/O) operations: The pattern is useful for adding features such as buffering, compression, or encryption to I/O streams.
  • Networking: It can be used to add functionalities like logging, caching, or authentication to network communication.

Advantages of the Decorator Pattern

  1. Flexibility: The pattern allows you to extend an object’s behavior dynamically without modifying its original structure.
  2. Modularity: It promotes the separation of concerns, making the code more maintainable and reusable.
  3. Scalability: New functionalities can be added easily by creating new decorators without affecting existing code.
  4. Single Responsibility Principle: Decorators can be designed to handle a single responsibility, adhering to the Single Responsibility Principle.

Disadvantages of the Decorator Pattern

  1. Complexity: The pattern can introduce complexity to the codebase, as it may require a large number of decorator classes.
  2. Confusing object hierarchy: It can be challenging to understand the object hierarchy, especially when multiple decorators are involved.
  3. Performance: There may be a slight performance overhead due to the added layers of decorators.

Real-World Examples

InputStream in JDK

A well-known example of the Decorator pattern in the Java Development Kit (JDK) is in the java.io package. The pattern is used for input/output stream classes, allowing you to easily add or modify the behavior of various stream classes.

The structure of the Decorator pattern in the java.io package includes the following elements:

  1. Component (InputStream, OutputStream, Reader, Writer): Abstract classes that define the basic interface for stream classes.
  2. Concrete Component (FileInputStream, FileOutputStream, FileReader, FileWriter): Concrete classes that implement the basic functionality.
  3. Decorator (FilterInputStream, FilterOutputStream, FilterReader, FilterWriter): Abstract classes that extend the base component class and can be used to add or modify behavior.
  4. Concrete Decorator (BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter): Concrete classes that implement specific functionality by extending the decorator class.

Here’s an example of using the Decorator pattern in the java.io package:

import java.io.*;

public class DecoratorExample {
public static void main(String[] args) {
try {
// Create a FileInputStream (concrete component)
FileInputStream fis = new FileInputStream("example.txt");

// Decorate the FileInputStream with a BufferedInputStream (concrete decorator)
BufferedInputStream bis = new BufferedInputStream(fis);

// Read data from the buffered input stream
int data;
while ((data = bis.read()) != -1) {
System.out.print((char) data);
}

// Close the streams
bis.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

In this example, we use a FileInputStream for reading a file, and then we decorate it with a BufferedInputStream to add buffering functionality. The BufferedInputStream allows us to read the data more efficiently by buffering the input.

GUI Components in AWT and Swing Package

The structure of the Decorator pattern in the java.awt and javax.swing packages includes the following elements:

  1. Component (Component, JComponent): Abstract classes that define the basic interface for GUI components.
  2. Concrete Component (JTextArea, JPanel, etc.): Concrete classes that implement the basic functionality of GUI components.
  3. Decorator (JScrollPane): A class that extends the JComponent class and can be used to add or modify behavior of the component it wraps.
  4. Concrete Decorator: In this case, JScrollPane acts as the concrete decorator by adding scrollbars to the wrapped component.

Here’s an example of using the Decorator pattern with the javax.swing package:

import javax.swing.*;
import java.awt.*;

public class SwingDecoratorExample {
public static void main(String[] args) {
// Create a JTextArea (concrete component)
JTextArea textArea = new JTextArea(5, 20);
textArea.setText("This is a JTextArea example using the Decorator pattern.\n\n");

// Add more lines to the JTextArea to show the scrollbar
for (int i = 1; i <= 20; i++) {
textArea.append("Line " + i + "\n");
}

// Decorate the JTextArea with a JScrollPane (concrete decorator)
JScrollPane scrollPane = new JScrollPane(textArea);

// Create a JFrame and add the JScrollPane containing the JTextArea
JFrame frame = new JFrame("Swing Decorator Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

In this example, we create a JTextArea and then use the JScrollPane class to decorate it with scrollbars. This allows the text area to be easily navigated when its content exceeds the visible area. The JScrollPane acts as a concrete decorator, adding specific behavior (scrollbars) to the JTextArea without modifying its original structure.

This demonstrates how the Decorator Design Pattern can be used in the context of GUI development, allowing you to easily create compound components and extend their functionality.

Conclusion

The Decorator Design Pattern is a powerful tool in any developer’s arsenal. It enables you to extend the functionality of objects dynamically without modifying their original structure or using inheritance. While it can introduce some complexity, the benefits of modularity, maintainability, and adherence to the Single Responsibility Principle often outweigh the drawbacks. By understanding and implementing the Decorator pattern, you can create more flexible and scalable applications.

Now that you have learned about the Decorator pattern and seen it in action with given examples from the JDK, you can start applying this pattern in your own projects to make your code more modular and maintainable. Happy coding!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Baran B.
Baran B.

Written by Baran B.

Software Architect | Java Engineer | Trip-Hop Listener | High-Level Reader

No responses yet

Write a response