Head First Design Patterns Series / 3 (Decorating Objects: The Decorator Pattern)

Building Extensible & Maintainable Object Oriented Software

Merve Arslan
Odeal-Tech
3 min readJan 30, 2023

--

The decorator pattern is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.

In this chapter, the authors explained the decorator pattern through the example of a corporate coffee chain.

In addition to the regular system, a variety of condiments such as steamed milk, soy and mocha (also known as chocolate) can be requested into the coffee, all of which can be topped with whipped milk. These changes need to be placed in ordering systems, as additional charges are required for all these changes.

Here’s their first attempt 🙃

Instead, you can start with a beverage and “decorate” it with seasonings at runtime. For example, if the client wants Dark Roast with Mocha and Whip:

1.) First, let’s consider the DarkRoast object

2.) The client wants Mocha, so we create a Mocha object that we wrap around the DarkRoast.

3.) Then we do the process in step 2 for Whip and Mocha.

4.) Now it’s time to calculate the cost for the customer. We do this by calling cost() in the outermost decorator, Whip and Whip will delegate the cost calculation to the objects it decorates. Mocha and Mocha will delegate the cost calculation to the objects they decorate. Let’s see how this works:

Now let’s see what we’ve learned looks like in the big picture.

Isn’t it much more tidy and understandable than picture 1?

Real-World Analogy

Wearing clothes is an example of using decorators. When you’re cold, you wrap yourself in a sweater. If you’re still cold with a sweater, you can wear a jacket on top. If it’s raining, you can put on a raincoat. All of these garments “extend” your basic behavior but aren’t part of you, and you can easily take off any piece of clothing whenever you don’t need it..

I want to end this article with the design principle in this chapter.

Our goal is to allow classes to be easily extended to incorporate new behavior without modifying existing code. What do we get if we accomplish this? Designs that are resilient to change and flexible enough to take on new functionality to meet changing requirements.

--

--