Deck the halls — The Design Pattern

I’m gonna give you a small scenario.

Say you own a coffee shop, and like any newbie, start out with just two types of plain coffee, the house blend, and dark roast. In your billing system, there was one class for the different coffee blends, which inherits the beverage abstract class. People actually start to come by and have your wonderful (albeit bitter😖) coffee. Then there are the coffee newbs that, God forbid, want sugar or milk. Such travesty to coffee!! 😱😱

Now you need to have those two add-ons as well, both to the menu, and unfortunately on the billing system. Originally, your IT guy will make a subclass for both coffees, one including sugar, the other milk. Then, since customers are always right, one says these dreaded words;

“Can I get a milk coffee, with sugar, please?”

😒😒😒

There goes your billing system laughing in your face again. Well, back to the drawing board….

The IT guy then adds milk coffee with sugar as another subclass to each parent coffee class. The rest of the month is smooth sailing, people lining up to have your coffee, you actually making money. 💸💰

But wait, there’s more!

The world is against you once again. A competitor opens up across the street, with not just 4 types of coffee, but more than 10 add-ons as well! 😤

You buy all those and more, to sell better coffee yourself, and just then remember that you forgot to update that dratted billing system. You quite possibly cannot make the infinite number of subclasses for any and all combinations of all the add-ons, with the new coffee blends too. Not to mention, the size of the final system.😩😩

Time to actually invest in a proper billing system. You find a new IT personnel, who actually knows what they are doing and they say;

“Why, this will be so much easier and smaller, if it used the decorator pattern.”

What on earth is that?

Well, its what this post is all about.

Decorator design pattern falls into the structural category, that deals with the actual structure of a class, whether is by inheritance, composition or both. The goal of this design is to modify an objects’ functionality at runtime. This is one of the many other design patterns that utilize abstract classes and interfaces with composition to get its desired result.

Let’s give Math a chance (shudder😖) to bring this all into perspective;

Take 4 coffee blends and 10 add-ons. If we stuck to the generation of subclasses for each different combination of all the add-ons for one type of coffee. That’s;

(10–1)² = 9² = 81 subclasses

We subtract 1 from the 10, as you cannot combine one add-on with another of the same type, sugar with sugar sounds stupid. And that’s for just one coffee blend. Multiply that 81 by 4 and you get a whopping 324 different subclasses! Talk about all that coding…

But with the decorator pattern will require only 16 classes in this scenario. Wanna bet?

Decorator Design Pattern Class diagram
Class diagram according to coffee shop scenario

If we map out our scenario according to the class diagram above, we get 4 classes for the 4 coffee blends, 10 for each add-on and 1 for the abstract component and 1 more for the abstract decorator. See! 16! Now hand over that $100.💸💰 (jk, but it will not be refused if given… just saying)

As you can see from above, just as the concrete coffee blends are subclasses of the beverage abstract class, the AddOn abstract class also inherits its methods from it. The add-ons, that are its subclasses, in turn inherit any new methods to add functionality to the base object when needed.

Let’s get to coding, to see this pattern in use.

First to make the Abstract beverage class, that all the different coffee blends will inherit from;

public abstract class Beverage {

private String description;
public
Beverage(String description) {
super();
this.description = description;
}
public String getDescription() {
return description;
}
public abstract double cost();
}

Then to add both the concrete coffee blend classes.

public class HouseBlend extends Beverage {  
public
HouseBlend() {
super(“House blend”);
}
@Override
public double cost() {
return 250;
}
}
public class DarkRoast extends Beverage {
public
DarkRoast() {
super(“Dark roast”);
}
@Override
public double cost() {
return 300;
}
}

The AddOn abstract class also inherits from the Beverage abstract class.(more on this below).

public abstract class AddOn extends Beverage {  
protected
Beverage beverage;
public
AddOn(String description, Beverage bev) {
super(description);
this.beverage = bev;
}
public
abstract String getDescription();
}

Now, to code those pesky add-ons.

public abstract class AddOn extends Beverage {
protected Beverage beverage;
public AddOn(String description, Beverage bev){
super(description);
this.beverage = bev;
}
public abstract String getDescription();
}

And now the concrete implementations of this abstract class;

public class Sugar extends AddOn { 

public
Sugar(Beverage bev) {
super(“Sugar”, bev);
}
@Override
public String getDescription() {
return beverage.getDescription() + “ with Mocha”;
}
@Override
public double cost() {
return beverage.cost() + 50;
}
}
public class Milk extends AddOn {
public
Milk(Beverage bev) {
super(“Milk”, bev);
}
@Override
public String getDescription() {
return beverage.getDescription() + “ with Milk”;
}

@Override
public double cost() {
return beverage.cost() + 100;
}
}

As you can see above, we can pass any subclass of Beverage to any subclass of AddOn, and get the added cost as well as the updated description. And, since the AddOn class is essentially of type Beverage, we can pass an AddOn into another AddOn. This way, we can add any number of add-ons to a specific coffee blend.

Now to write some code to test this out.

public class CoffeeShop {  
public
static void main(String[] args) {
HouseBlend houseblend = new HouseBlend();
System.out.println(houseblend.getDescription() + “: “ +
houseblend.cost());
Milk milkAddOn = new Milk(houseblend); System.out.println(milkAddOn.getDescription() + “: “ +
milkAddOn.cost());
Sugar sugarAddOn = new Sugar(milkAddOn); System.out.println(sugarAddOn.getDescription() + “: “ +
sugarAddOn.cost());
}
}

The final result is;

P.S. this is in Sri Lankan Rupees

It works! We were able to add more than one add-on to a coffee blend and successfully update its final cost and description, without the need to make infinite subclasses for each add-on combination for all coffee blends.

That’s all for today, join me next time for a post that commands 😁 your attention.

Find the commit to this post, here.

Check out my other Design Pattern posts;

Front End Developer at Switchd Ltd. | MSc in Computer Science @ QMUL (2022)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store