DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Architecture and Code Design, Pt. 2: Polyglot Persistence Insights To Use Today and in the Upcoming Years
  • Double-Checked Locking Design Pattern in Java
  • Messaging Design Pattern (MDP) In Java

Trending

  • Rust and WebAssembly: Unlocking High-Performance Web Apps
  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • Segmentation Violation and How Rust Helps Overcome It
  • Chaos Engineering for Microservices
  1. DZone
  2. Coding
  3. Java
  4. Decorator Design Pattern in Java

Decorator Design Pattern in Java

Want to learn more about using the decorator design pattern in Java? Check out this example using the Shape class to learn how.

By 
Brijesh Saxena user avatar
Brijesh Saxena
DZone Core CORE ·
Aug. 23, 18 · Tutorial
Likes (43)
Comment
Save
Tweet
Share
152.4K Views

Join the DZone community and get the full member experience.

Join For Free

Today, I am discussing one of the well-known and frequently used patterns called the decorator design pattern.

Decorator Design Pattern

The decorator design pattern allows us to dynamically add functionality and behavior to an object without affecting the behavior of other existing objects in the same class. 

We use inheritance to extend the behavior of the class. This takes place at compile time, and all of the instances of that class get the extended behavior.

Decorator design patterns allow us to add functionality to an object (not the class) at runtime, and we can apply this customized functionality to an individual object based on our requirement and choice.

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

  • 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 provide additional functionality by keeping the class methods' signature unchanged.

  • Decorator design patterns are most often used for applying single responsibility principles since we divide the functionality into classes with unique areas of concern.

  • The decorator design pattern is structurally similar to the chain of responsibility pattern.

Let's take a look at an example to better understand the pattern.

Steps to Implementing the Decorator Design Pattern

  • Suppose, we have a Shape interface, which can includedraw(), resize() ,  isHide(), anddescription().

package design.decorator;

public interface Shape {
    void draw();
    void resize();
    String description();
    boolean isHide();
}


  • Now, we have two concrete classes of Shape  — Circle and Rectangle — to define a specific shape.

Below is the code for Circle: 

package design.decorator;

public class Circle implements Shape {

    @Override
    public void draw() {
    System.out.println("Drawing Circle");
    }

    @Override
    public void resize() {
    System.out.println("Resizing Circle");
    }

    @Override
    public String description() {
    return "Circle object";
    }

    @Override
    public boolean isHide() {
    return false;
    }

}


Below is the code forRectangle:

package design.decorator;

public class Rectangle implements Shape {

    @Override
    public void draw() {
    System.out.println("Drawing Rectangle");
    }

    @Override
    public void resize() {
    System.out.println("Resizing Rectangle");
    }

    @Override
    public String description() {
    return "Rectangle object";
    }

    @Override
    public boolean isHide() {
    return false;
    }

}


  • Now, let's take a look at the decoration portion. So far, all is good, and we can draw Circle and Rectangle. But, we would like to have some additional functionalities for the Shape, like Fill-Color, Line-Color,  Line-Thinkness, Line-Style, and so on.

  • First, we will create an abstract wrapper (decorator) class that implements the Shape. I will use theShapeDecorator for this example.

package design.decorator;

public abstract class ShapeDecorator implements Shape {
      protected Shape decoratedShape;

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

}


  • I kept this abstract to avoid any direct instantiation since this is just a wrapper and does not add any functionality into the shape. Also, I have implemented Shape to allow adding additional functionalities to all of the defined, concrete Shape classes — Circle andRectanagle — for this case.

  • Create enums for  Color and LineStyle for shapes. Below is the enum for  Color:

package design.decorator;

public enum Color {
      RED,
      GREEN,
      BLUE,
      YELLOW,
      WHITE,
      BLACK,
      ORANGE,
      MAROON
}


  • Below is the enum for  LineStyle:

package design.decorator;

public enum LineStyle {
      SOLID,
      DASH,
      DOT,
      DOUBLE_DASH,
      DASH_SPACE
}


  • Create the FillColorDecorator to add the functionality of the fill-color in the shape.

package design.decorator;

public class FillColorDecorator extends ShapeDecorator {

      protected Color color;

      public FillColorDecorator(Shape decoratedShape, Color color) {
            super(decoratedShape);
            this.color = color;
      }

      @Override
      public void draw() {
            decoratedShape.draw();
            System.out.println("Fill Color: " + color);
      }

      // no change in the functionality
      // we can add in the functionality if we like. there is no restriction
      // except we need to maintain the structure of the Shape APIs
      @Override
      public void resize() {
      decoratedShape.resize();
      }

      @Override
      public String description() {
      return decoratedShape.description() + " filled with " + color + " color.";
      }

      // no change in the functionality
      @Override
      public boolean isHide() {
      return decoratedShape.isHide();
      }

}


  • Create the  LineColorDecorator to add the functionality of line-color in the shape.

package design.decorator;

public class LineColorDecorator extends ShapeDecorator {

      protected Color color;

      public LineColorDecorator(Shape decoratedShape, Color color) {
      super(decoratedShape);
      this.color = color;
      }

      @Override
      public void draw() {
      decoratedShape.draw();
      System.out.println("Line Color: " + color);
      }

      // no change in the functionality
      @Override
      public void resize() {
      decoratedShape.resize();
      }

      @Override
      public String description() {
      return decoratedShape.description() + " drawn with " + color + " color.";
      }

      // no change in the functionality
      @Override
      public boolean isHide() {
      return decoratedShape.isHide();
      }

}


  • Create the  LineThicknessDecorator to add the functionality of the custom line thickness in the shape.

package design.decorator;

public class LineThinknessDecorator extends ShapeDecorator {

      protected double thickness;

      public LineThinknessDecorator(Shape decoratedShape, double thickness) {
                super(decoratedShape);
                this.thickness = thickness;
      }

      @Override
      public void draw() {
                decoratedShape.draw();
                System.out.println("Line thickness: " + thickness);
      }

      // no change in the functionality
      @Override
      public void resize() {
      decoratedShape.resize();
      }

      @Override
      public String description() {
      return decoratedShape.description() + " drawn with line thickness " + thickness + ".";
      }

      // no change in the functionality
      @Override
      public boolean isHide() {
      return decoratedShape.isHide();
      }

}
  • Create LineStyleDecorator to add functionality of custom line styles in the shape.

package design.decorator;

public class LineStyleDecorator extends ShapeDecorator {

        protected LineStyle style;

        public LineStyleDecorator(Shape decoratedShape, LineStyle style) {
        super(decoratedShape);
        this.style = style;
        }

        @Override
        public void draw() {
        decoratedShape.draw();
        System.out.println("Line Style: " + style);
        }

        // no change in the functionality
        @Override
        public void resize() {
        decoratedShape.resize();
        }

        @Override
        public String description() {
        return decoratedShape.description() + " drawn with " + style + " lines.";
        }

        // no change in the functionality
        @Override
        public boolean isHide() {
        return decoratedShape.isHide();
        }

}


  • Create a sample Main program to execute and test the decorator code.

package design.decorator;

public class Main {

  public static void main(String[] args) {

            System.out.println("Creating Simple Shape Objects...");
            Shape rectangle = new Rectangle();
            Shape circle = new Circle();

            System.out.println("Drawing Simple Shape Objects...");
            rectangle.draw();
            System.out.println();
            circle.draw();
            System.out.println();

            System.out.println("Creating Decorated Circle with Red Color, Blue Lines in dash pattern and thickness of 2 ...");
            Shape circle1 = new FillColorDecorator(new LineColorDecorator(new LineStyleDecorator(
            new LineThinknessDecorator(new Circle(), 2.0d), LineStyle.DASH), Color.BLUE), Color.RED);
            circle1.draw();
            System.out.println();
            // order of decorator is also not much important here since all are unique functionalities.
            // we can also do this nesting of functionalities in separate statements.
            System.out.println("creating object with similar functionalities in separate statements.");
            Circle c = new Circle();
            LineThinknessDecorator lt = new LineThinknessDecorator(c, 2.0d);
            LineStyleDecorator ls = new LineStyleDecorator(lt, LineStyle.DASH);
            LineColorDecorator lc = new LineColorDecorator(ls, Color.BLUE);
            FillColorDecorator fc = new FillColorDecorator(lc, Color.RED);
            Shape circle3 = fc;
            circle3.draw();
            System.out.println();

            System.out.println("Creating Decorated Circle with Green Color, Black Lines ...");
            Shape circle2 = new FillColorDecorator(new LineColorDecorator(new Circle(), Color.BLACK), Color.GREEN);
            circle2.draw();
            System.out.println();

            System.out.println("Creating Decorated Rectange with Yellow Color, Red Lines in double dash pattern...");
            Shape rectangle1 = new FillColorDecorator(new LineColorDecorator(new Rectangle(), Color.RED), Color.YELLOW);
            rectangle1.draw();
            System.out.println();

      }

}


  • Below is the output of the code:

Creating Simple Shape Objects...
Drawing Simple Shape Objects...
Drawing Rectangle

Drawing Circle

Creating Decorated Circle with Red Color, Blue Lines in dash pattern and thickness of 2 ...
Drawing Circle
Line thickness: 2.0
Line Style: DASH
Line Color: BLUE
Fill Color: RED

creating object with similar functionalities in separate statements.
Drawing Circle
Line thickness: 2.0
Line Style: DASH
Line Color: BLUE
Fill Color: RED

Creating Decorated Circle with Green Color, Black Lines ...
Drawing Circle
Line Color: BLACK
Fill Color: GREEN

Creating Decorated Rectange with Yellow Color, Red Lines in double dash pattern...
Drawing Rectangle
Line Color: RED
Fill Color: YELLOW


As we can see, we have not changed the Core classes, Shape , Circle, and  Rectangle. By creating the wrapper and decorator classes, we have added and customized the behavior of Shape, Circle, and Rectangle. There you have it. I hope we are clear on the decorator design pattern now.

Liked the article? Don't forget to press that like button. Happy coding!

Need more articles on Design Patterns? Below are some of them I have shared with you.

  • Null Object Pattern in Java
  • Using the Adapter Design Pattern in Java

  • Using the Bridge Design Pattern in Java

  • Strategy vs. Factory Design Patterns in Java

  • Decorator Design Pattern in Java

  • How to Use Singleton Design Pattern in Java

  • Singleton Design Pattern: Making Singleton More Effective in Java

Some additional Articles:

  • Java Enums: How to Make Enums More Useful

  • Java Enums: How to Use Configurable Sorting Fields

Design Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Architecture and Code Design, Pt. 2: Polyglot Persistence Insights To Use Today and in the Upcoming Years
  • Double-Checked Locking Design Pattern in Java
  • Messaging Design Pattern (MDP) In Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!