Open In App

Decorator Method Design Patterns in Java with Example

Improve
Improve
Like Article
Like
Save
Share
Report

Decorator design pattern allows us to dynamically add functionality and behavior to an object without affecting the behavior of other existing objects within the same class. We use inheritance to extend the behavior of the class. This takes place at compile-time, and all the instances of that class get the extended behavior.

  • Decorator patterns allow a user to add new functionality to an existing object without altering its structure. So, there is no change to the original class.
  • The decorator design pattern is a structural pattern, which provides a wrapper to the existing class.
  • The decorator design pattern uses abstract classes or interfaces with the composition to implement the wrapper.
  • Decorator design patterns create decorator classes, which wrap the original class and supply additional functionality by keeping the class methods’ signature unchanged.
  • Decorator design patterns are most frequently used for applying single responsibility principles since we divide the functionality into classes with unique areas of concern.
  • The decorator design pattern is structurally almost like the chain of responsibility pattern.

Remember: Certain key points are to be taken into consideration that are as follows: 

  1. Decorator design pattern is useful in providing runtime modification abilities and hence more flexible. Its easy to maintain and extend when the amount of choices are more.
  2. The disadvantage of decorator design pattern is that it uses plenty of similar kind of objects (decorators)
  3. Decorator pattern is used a lot in Java IO classes, like FileReader, BufferedReader, etc.

Procedure:

  1. Create an interface.
  2. Create concrete classes implementing the same interface.
  3. Create an abstract decorator class implementing the above same interface.
  4. Create a concrete decorator class extending the above abstract decorator class.
  5. Now use the concrete decorator class created above to decorate interface objects.
  6. Lastly, verify the output

Implementation:

We’re going to create a Shape interface and concrete classes implementing the Shape interface. We will then create an abstract decorator class ShapeDecorator implementing the Shape interface and having the Shape object as its instance variable.

  1. ‘Shape’ is the name of the interface
  2. ‘Rectangle’ class and ‘Circle’ class will be concrete classes implementing the ‘Shape’ interface.
  3. ‘ShapeDecorator’ is our abstract decorator class implementing the same ‘Shape’ interface.
  4. RedShapeDecorator is a concrete class implementing ShapeDecorator.
  5. DecoratorPatternDemo, our demo class will use RedShapeDecorator to decorate Shape objects.

Step 1: Creating an interface named ‘Shape’

Example

Java




// Interface named Shape
public interface Shape {
 
    // Method inside interface
    void draw();
}


Step 2: Create concrete classes implementing the same interface. Rectangle.java and Circle.java are as follows

Example 

Java




// Class 1
// Class 1 will be implementing the Shape interface
 
// Rectangle.java
public class Rectangle implements Shape {
 
    // Overriding the method
    @Override public void draw()
    {
        // /Print statement to execute when
        // draw() method of this class is called
        // later on in the main() method
        System.out.println("Shape: Rectangle");
    }
}


Java




// 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.

Example 

Java




// Class 2
// Abstract class
// ShapeDecorator.java
public abstract class ShapeDecorator implements Shape {
 
    // Protected variable
    protected Shape decoratedShape;
 
    // Method 1
    // Abstract class method
    public ShapeDecorator(Shape decoratedShape)
    {
        // This keywordd refers to current object itself
        this.decoratedShape = decoratedShape;
    }
 
    // Method 2 - draw()
    // Outside abstract class
    public void draw() { decoratedShape.draw(); }
}


 
Step 4: Create a concrete decorator class extending the ShapeDecorator class.

Example 

Java




// Class 3
// Concrete class extending the abstract class
// 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)
    {
      // Display message whenever function is called
        System.out.println("Border Color: Red");
    }
}


 
Step 5: Using the RedShapeDecorator to decorate Shape objects.

Example 

Java




// DecoratorPatternDemo.java
 
// Class
// Main class
public class DecoratorPatternDemo {
 
    // Main driver method
    public static void main(String[] args)
    {
        // Creating an object of Shape interface
        // inside the main() method
        Shape circle = new Circle();
 
        Shape redCircle
            = new RedShapeDecorator(new Circle());
 
        Shape redRectangle
            = new RedShapeDecorator(new Rectangle());
 
        // Display message
        System.out.println("Circle with normal border");
 
        // Calling the draw method over the
        // object calls as created in
        // above classes
 
        // Call 1
        circle.draw();
 
        // Display message
        System.out.println("\nCircle of red border");
 
        // Call 2
        redCircle.draw();
 
        // Display message
        System.out.println("\nRectangle of red border");
 
        // Call 3
        redRectangle.draw();
    }
}


 
Step 6: Verifying the output

Output: 

Circle with normal border
Shape: Circle
Circle of red border
Shape: Circle
Border Color: Red
Rectangle of red border
Shape: Rectangle
Border Color: Red

Output explanation:  

Glancing at the decorator design pattern one can conclude out that this is often a decent choice in the following cases where

  • When we wish to add, enhance or perhaps remove the behavior or state of objects.
  • When we just want to modify the functionality of a single object of the class and leave others unchanged.

Further Read: Java Design Patterns Tutorial



Last Updated : 07 Nov, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads