Easy Tutorial
❮ Intercepting Filter Pattern Design Pattern Intro ❯

Decorator Pattern

The Decorator Pattern allows new functionalities to be added to an existing object without altering its structure. This type of design pattern is a structural pattern, as it acts as a wrapper to an existing class.

This pattern creates a decorator class that wraps the original class and provides additional functionality while keeping the class method signatures intact.

We will demonstrate the usage of the Decorator Pattern through the following example. Here, we will decorate a shape with different colors without changing the shape class.

Introduction

Intent: Dynamically add additional responsibilities to an object. In terms of adding functionality, the Decorator Pattern is more flexible than subclassing.

Main Solution: Typically, to extend a class, we often use inheritance. However, inheritance introduces static features to the class and can lead to excessive subclass proliferation as more functionalities are added.

When to Use: When you want to extend a class without creating many subclasses.

How to Solve: Divide specific functional responsibilities and use the decorator pattern.

Key Code: 1. The Component class acts as an abstract role and should not be implemented concretely.

  1. The decorator class references and inherits the Component class, and the concrete extension class overrides the parent class methods.

Application Examples: 1. Sun Wukong has 72 transformations. When he turns into a "temple," he is still fundamentally a monkey but has acquired temple functionalities.

  1. A painting can be hung on the wall with or without a frame, but typically it has a frame. The frame is actually the one hung on the wall. Before being hung, the painting can be covered with glass and placed in a frame; at this point, the painting, glass, and frame form a single object.

Advantages: The decorator and the decorated class can develop independently without being tightly coupled. The Decorator Pattern is an alternative to inheritance and allows for the dynamic expansion of an implementation class's functionalities.

Disadvantages: Multi-layer decoration can be complex.

Usage Scenarios: 1. Extending a class's functionality.

  1. Dynamically adding and removing functionalities.

Notes: Can replace inheritance.

Implementation

We will create a Shape interface and concrete classes implementing the Shape interface. Then, we will create an abstract decorator class ShapeDecorator that implements the Shape interface and takes a Shape object as its instance variable.

RedShapeDecorator is a concrete class implementing ShapeDecorator.

The DecoratorPatternDemo class uses RedShapeDecorator to decorate Shape objects.

Step 1

Create an interface:

Shape.java

public interface Shape {
   void draw();
}

Step 2

Create concrete classes implementing the interface.

Rectangle.java

public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Shape: Rectangle");
   }
}

Circle.java

public class Circle implements Shape {
   @Override
   public void draw() {
      System.out.println("Shape: Circle");
   }
}

Step 3

Create an abstract decorator class implementing the Shape interface.

ShapeDecorator.java

public abstract class ShapeDecorator implements Shape {
   protected Shape decoratedShape;

   public ShapeDecorator(Shape decoratedShape){
      this.decoratedShape = decoratedShape;
   }

   public void draw(){
      decoratedShape.draw();
   }  
}

Step 4

Create concrete decorator classes extending ShapeDecorator.

RedShapeDecorator.java

public class RedShapeDecorator extends ShapeDecorator {
   public RedShapeDecorator(Shape decoratedShape) {
      super(decoratedShape);     
   }

   @Override
   public void draw() {
      decoratedShape.draw();         
      setRedBorder(decoratedShape);
   }

   private void setRedBorder(Shape decoratedShape){
      System.out.println("Border Color: Red");
   }
}

Step 5

Use RedShapeDecorator to decorate Shape objects.

DecoratorPatternDemo.java

public class DecoratorPatternDemo {
   public static void main(String[] args) {
      Shape circle = new Circle();
      ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
      ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());

      System.out.println("Circle with normal border");
      circle.draw();

      System.out.println("\nCircle of red border");
      redCircle.draw();

      System.out.println("\nRectangle of red border");
      redRectangle.draw();
   }
}

Step 6

Execute the program, and the output will be:

Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border Color: Red
❮ Intercepting Filter Pattern Design Pattern Intro ❯