Decorator Design Pattern in Java

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.

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

Steps to Implementing the Decorator Design Pattern

package design.decorator;

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


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;
    }

}


package design.decorator;

public abstract class ShapeDecorator implements Shape {
      protected Shape decoratedShape;

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

}


package design.decorator;

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


package design.decorator;

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


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();
      }

}


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();
      }

}


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();
      }

}
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();
        }

}


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();

      }

}


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 ShapeCircle, 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.

Some additional Articles:

 

 

 

 

Top