Decorator Pattern

Decorator Pattern

Publish Date: Nov 4 '24
76 7

What is Decorator Pattern?

Decorator pattern is a structural design pattern that attaches additional behaviors to an object dynamically. Decorators provide a flexible extension reason by composition rather than subclassing (inheritance).

When to use it?

Use Decorator pattern when you have many possible combination to construct an object.

Problem

Imagine we're developing a system for an ice cream shop. The shop has various ice creams and toppings. The system needs to display an ice cream description (including its toppings) and cost.

Our first version system is this:

Image description

This seems fine, but what if we could add toppings?

Our second version:

Image description

The problem is unveiled! Notice even they are not enough since we need ChocolateIceCreamWithChocolateChipsAndMapleNuts and so on.

Solution

Image description

Decorator Pattern is also known as Wrapper Pattern since it works by wrapping additional functionality around an object. Wrapped object (ice cream) is called Component, and Wrapper object (topping) is called Decorator.

  1. IceCream class
    Components and decorators have common interface IceCream class (you will see why later), they both declare as IceCream object.

  2. Concrete IceCream classes
    Each concrete ice cream overrides cost method since prices are different for each.

  3. Topping class
    Topping class provides interface for concrete toppings and holds reference to an IceCream object.

  4. Concrete Topping classes
    If system needs another topping, say caramel source, what you need to do is just creating CaramelSource class which extends Topping class.

Structure

Image description

Decorator class uses composition and inheritance, it's crucial to understand their intent.
In Decorator pattern, we use the same type for both components & decorators. Decorator composites Component object to get behavior, that is, obtaining fields or methods defined in Component object. While Decorator inherits (extends) Component so that Decorator object can be declared as Component object.

Decorator pattern achieves open-closed principle, namely, open for extension and closed for modification. It's easy to add components or decorators. for instance, if you want to add another concrete decorator, you just need to create a class representing it and extends Decorator class.

Implementation in Java

// Component class
public abstract class IceCream {

    public String description = "Unknown ice cream";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}
Enter fullscreen mode Exit fullscreen mode
// Concrete component class
public class ChocolateIceCream extends IceCream {

    public ChocolateIceCream() {
        description = "ChocolateIceCream";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}
Enter fullscreen mode Exit fullscreen mode
// Base decorator class
public abstract class Topping extends IceCream {

    public IceCream iceCream;

    // All subclasses (concrete decorator classes) need to implement getDescription method,
    // by declaring this method as abstract, we enforce all subclasses to implement this method
    public abstract String getDescription();
}
Enter fullscreen mode Exit fullscreen mode
// Concrete decorator class
public class MapleNuts extends Topping {

    public MapleNuts(IceCream iceCream) {
        this.iceCream = iceCream;
    }

    @Override
    public String getDescription() {
        return iceCream.getDescription() + ", MapleNuts";
    }

    @Override
    public double cost() {
        return iceCream.cost() + .30;
    }
}
Enter fullscreen mode Exit fullscreen mode
// Concrete decorator class
public class PeanutButterShell extends Topping {

    public PeanutButterShell(IceCream iceCream) {
        this.iceCream = iceCream;
    }

    @Override
    public String getDescription() {
        return iceCream.getDescription() + ", PeanutButterShell";
    }

    @Override
    public double cost() {
        return iceCream.cost() + .30;
    }
}
Enter fullscreen mode Exit fullscreen mode
public class Client {

    public static void main(String[] args) {
        IceCream iceCream = new ChocolateIceCream();
        System.out.println(iceCream.getDescription() + ", $" + iceCream.cost());

        iceCream = new MapleNuts(iceCream);
        System.out.println(iceCream.getDescription() + ", $" + iceCream.cost());

        iceCream = new PeanutButterShell(iceCream);
        System.out.println(iceCream.getDescription() + ", $" + iceCream.cost());
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

ChocolateIceCream, $1.99
ChocolateIceCream, MapleNuts, $2.29
ChocolateIceCream, MapleNuts, PeanutButterShell, $2.59
Enter fullscreen mode Exit fullscreen mode

Pitfalls

  • Often ends up in large number of classes being added to the system, where each class provides small behavior.
  • You needs additional work if you want to let each decorator to know inner decorators. In above example, if you want to print ChocolateIceCream, MapleNuts * 2 instead of ChocolateIceCream, MapleNuts, MapleNuts, each decorators needs to know which toppings have been added so far (can be done by ArrayList for example).

You can check all the design pattern implementations here.
GitHub Repository


P.S.
I'm new to write tech blog, if you have advice to improve my writing, or have any confusing point, please leave a comment!
Thank you for reading :)

Comments 7 total

  • martipello
    martipelloNov 5, 2024

    Yh this could do with a lot of work, your first paragraph is a word salad and barely comprehensible

    • Micheal Kinney
      Micheal KinneyNov 5, 2024

      Yeah, I read it like three times and just moved on hoping the article would explain it. But kind of just abruptly ended. ChatGPT?

    • Sota
      SotaNov 6, 2024

      Thank you for your review!
      I'll edit and try to improve this article.

  • Кирилл Петров
    Кирилл ПетровNov 5, 2024

    I had an amazing experience at European Casinos not on Gamstop UK! The site uk.nongamstopcasinos.net/european-... is easy to navigate, and the variety of games is impressive. I loved the wide selection of slots and table games. The customer support team is friendly and responsive, making everything hassle-free. If you're looking for a great online casino experience in the UK, I highly recommend this one!

  • Chris Mcdonald
    Chris McdonaldNov 7, 2024

    The explanation of the Decorator Pattern is quite insightful.
    What are some practical scenarios where you found using the Decorator Pattern especially beneficial? Are there any common pitfalls developers should watch out for when implementing this pattern?

    • Sota
      SotaNov 11, 2024

      Thank you for your review!
      I edited this blog and tried to introduce concrete example in "Problem" section.
      I also mentioned about pitfalls as well.

      • John Watts
        John WattsNov 12, 2024

        The classic example of a real world use is the various reader classes in Java's java.io package. You've given a good toy example and explanation of the pattern, good work 👍🏼

Add comment