Design Patterns: The Decorator Pattern

Design Patterns: The Decorator PatternLearn how to make your code cleaner and more modular with the Decorator patternNitin VermaBlockedUnblockFollowFollowingMay 6Prerequisites:If you’re not not familiar with the term ‘design patterns,’ make sure that to check out my Introduction to design patterns.

You understand Java or any other OOP language.

You have a basic idea about inheritance, polymorphism, and interfaces.

To see the full code, please check my GitHub repository:Ni3verma/Coffe-ShopGithub repository for my medium post to explain Decorator patterngithub.

comSuppose that you have a coffee shop, and it’s the fastest growing shop around.

Just look across the street and you will see it’s other branch.

You serve four types of beverages: house blend, dark roast, decaf, and espresso.

When you first started the business, you designed your classes like this:Beverage is abstract class and cost method is also abstract in it.

Subclasses will implement it.

Beverage has four sub-classes: HouseBlend, DarkRoast, Decaf, and Espresso.

All of them override cost method and return their own cost.

Here, we have used inheritance.

In addition to coffee, you can also ask for condiments like steamed milk, soy, mocha, and whipped milk.

You can implement it similar to before and use inheritance.

This way we will have a huge number of classes: HouseBlendMilk, HouseBlendMocha, HouseBlendSoy, HouseBlendWhip, HouseBlendMilkMocha, HouseBlendMilkSoy, etc.

The list goes on.

There will be around 100 classes that we will have to make.

That’s what happen when we overdo inheritance.

It can easily result in class explosion.

There is clearly a better way to implement like this.

Let’s find one.

Approach 1:To see code related to approach 1, click below:Ni3verma/Coffe-Shopgithub.

comor if you have already cloned the repository, check out simple approach commit.

In this approach we will add some boolean variables in Beverage class as flags.

If it’s true, it means that condiment is present, so add its cost.

This time cost() method is not abstract.

In our concrete Beverage class like DarkRoast, we will override the cost method and add cost after calculating condiment cost by calling super.

cost().

Let’s say for Dark roast coffee, we will add 20 extra rupees.

@Override public int cost(){ return super.

cost() + 20; }Here’s how we will make HouseBlend milk coffee.

Its output will be cost of house blend coffee + cost of steamed milk (condiment).

Beverage b = new HouseBlend(); b.

setMilk(true); System.

out.

println("total cost = "+b.

cost());Our code will work perfectly, but there are few problems with this simple approach:If there is a price change for condiment, or a new condiment is to be added, we will have to modify our existing code and make new methods.

Note that adding new things should not alter existing code, as it may result in bugs.

We may have some new beverages, like iced tea.

For this, there is no point of adding milk condiment, but we will inherit it from Beverage class.

What if customer wants a double Mocha?Approach 2:To see code related to it, checkout this commit.

In this approach, we will use decorator pattern.

In this design pattern we have a component (like DarkRoast) and decorators (like Whip, Mocha, etc.

).

We wrap this component with decorators.

This is what I mean by wrapping:Now, when we want to compute the cost, we will call cost method on the outermost decorator, and then it will call cost method of its inner decorator/component.

This will continue, until it reaches its component (DarkRoast here).

Then DarkRoast will return its own cost to Mocha, Mocha will add its own cost to it and return the total to Whip.

Whip will add its own cost, and return the total out.

This is the flow:Cost of DarkRoast = 20, Mocha = 50, and Whip = 60.

Total cost is 130 rupeesThe Decorator pattern attaches additional responsibilities to an object dynamically.

It provides a flexible alternative to subclassing for extending a functionality.

Now that we have some ideas in our mind about how this pattern works, let’s see how we will implement this type of behavior.

Component: It is a abstract class; both concrete components and Decorator abstract class will inherit its behavior.

Concrete Component: extends Component.

It is the object to which we are going to dynamically add new behavior.

It can be used on its own or wrapped by a decorator.

(For example, we can order DarkRoast coffee or DarkRoast with Mocha.

)Decorator: It also extends component and is an abstract class.

Concrete Decorator: Each decorator has a component, which means that it has an instance variable that holds reference to a component (the dotted line represents this).

It extends Decorator abstract class, like Milk, Soy, etc.

Now let’s see our abstract Component class, in our case Beverage.

java file:The cost method is abstract because we want all of the classes that inherit it (concrete components and decorators) to override it and provide their own cost.

Now let’s see one of our concrete component class:Whenever we define a concrete component, we will give it description in constructor and override cost method and return its cost.

Now it’s time to see the main class where magic happens.

Let’s see the abstract decorator class; in our case, it’s CondimentDecorator.

java.

Note that it also extends Beverage.

The getDescription method is abstract because we want all of the concrete decorators to override it and provide their own implementation for it.

As we have not implemented cost method here, every concrete decorator will have to override it also.

Additionally, we have a instance variable beverage because we will require it in concrete decorator class.

Only because of this will we be able to form a chain of call to parent’s cost method like this:Let’s have a look at one of concrete decorator class, Mocha.

java:In the constructor, it will receive a Beverage and set its instance variable to it and override the methods.

Note that the beverage variable is inherited from the CondimentDecorator class.

Before ordering coffee, remember that both decorators (Whip, Soy) and components (DarkRoast, Decaf) extends component (Beverage) class.

So we can say that:DarkRoast is a Beverage.

DarkRoast with Mocha is also a Beverage.

DarkRoast with Mocha and Whip is also a Beverage.

Now let’s order some coffee!.????.In your main method, test this code:Beverage b1 = new Espresso();System.

out.

println(b1.

getDescription() + " Rs.

" + b1.

cost());// ** OUTPUT **// Espresso Rs.

30Ordering a simple coffee is easy and simple; let’s add a condiment.

Beverage b2 = new DarkRoast();b2 = new Mocha(b2);System.

out.

println(b2.

getDescription() + " Rs.

" + b2.

cost());// ** OUTPUT **// Dark roast, Mocha Rs.

70// Rs 70 = Rs 20(of DarkRoast) + Rs 50(of Mocha)b2 = new Mocha(b2).

This is what we mean by decorating a object.

We have taken a object of DarkRoast and decorated with the Mocha class.

Remember that the concrete decorator (Mocha here) class receives a beverage in its constructor?.Here we have passed b2.

So you can say that in the Mocha class beverage = b2, and at this time b2 was an object of theDarkRoast class.

When we compute the cost, beverage.

cost will return the cost of b2 (DarkRoast = Rs 20), and then we add 50 in it, so the result is Rs 70.

Now let’s add two condiments, Mocha and Whip:Beverage b2 = new DarkRoast();b2 = new Mocha(b2); // wrap it with mochab2 = new Whip(b2); // wrap it with whipSystem.

out.

println(b2.

getDescription() + " Rs.

" + b2.

cost());// ** OUTPUT **// Dark roast, Mocha, Whip Rs.

130// Rs 130 = Rs 20(of DarkRoast) + Rs 50(of Mocha) + Rs 60(of Whip)As I have already told you, (DarkRoast +Mocha) is also a beverage.

This is the reason that we can pass b2 object in Whip constructor.

Again, the flow is similar as before.

When we call b2.

cost(), first it will compute beverage.

cost(), and for Whip, beverage is DarkRoast + Mocha.

When we call beverage.

cost() on this DarkRoast + Mocha, it will call its beverage.

cost(), and for (DarkRoast + Mocha) system, beverage is DarkRoast.

Here beverage.

cost() will give us 20; we add 50 (Mocha cost) to it and return 70.

This 70 is received by Whip, and it adds 60 and returns 130.

Beverage b2 = new DarkRoast();b2 = new Mocha(b2); // wrap it with mochab2 = new Whip(b2); // wrap it with whipI know that the above part of the code is a little bit confusing.

But you’ll learn to decorate objects in a much better way when you learn about the Factory and Builder patterns.

Some of you might be saying that you have never seen code like this.

But if you have ever written a piece of code in Java, then to take user input, you either use Scanner or InputStreamReader or some other method.

Let’s see how to read user input by InputStreamReader:BufferedReader br = new BufferedReader(new InputStreamReader(System.

in));String s = br.

readLine();Here we are wrapping an InputStreamReader within a BufferedReader so you have been using Decorator pattern from the beginning without even knowing about it!Last important point to note is that, although Decorator is a subclass of the Component class, we can not directly make objects of concrete decorator without passing in a concrete component.

For example, here we cannot directly order only Mocha because it requires a Beverage to pass in constructor.

Mocha is a decorator so it must be used to decorate a Beverage.

So first make a object of concrete component like DarkRoast, Decaf, or some other coffee and then decorate it with decorators.

Thank you so much for sticking with me for this long.

Hope you learned about decorators and components.

ReferencesHead First Design Patterns (Amazon)My GitHub repo for this article.

. More details

Leave a Reply