Today we gonna talk about Decorator Pattern.
Every morning, before starting work I drink coffee, sometimes with milk, sometimes with a bar of chocolate.
Let’s imagine the business that seels coffee asked us to write software that will help them selling different types of coffee (Coffe with Milk
, Coffe with Chocolate
, Coffe with Milk and Chocolate
etc).
Our task is to implement a flexible system, the business can ask us to add new ingredients even before delivering software.
For a flexible system that allows decorating beverage with different ingredients, we will use Decorator Pattern
:

interface Beverage { String getDescription(); BigDecimal cost(); }
And will implement few base beverages:
class Coffee implements Beverage { private final String description = "Coffee"; private final BigDecimal price = BigDecimal.valueOf(129, CoffeeShop.DEFAULT_SCALE); @Override public String getDescription() { return description; } @Override public BigDecimal cost() { return price; } @Override public String toString() { return "Coffee{" + "description='" + description + '\'' + ", price=" + price + '}'; } }
class Tea implements Beverage { private final String description = "Tea"; private final BigDecimal price = BigDecimal.valueOf(99, CoffeeShop.DEFAULT_SCALE); @Override public String getDescription() { return description; } @Override public BigDecimal cost() { return price; } @Override public String toString() { return "Tea{" + "description='" + description + '\'' + ", price=" + price + '}'; } }
Now it’s time to prepare ingredients that we can use to decorate our beverages, every time when a new ingredient is added to the beverage, the beverage price changes and the description reflects this.
Observe that ingredients also implements Beverage
interface and they are receiving a reference to the beverage that should be decorated
:
class MilkDecorator implements Beverage { private final String description = "Milk"; private final BigDecimal price = BigDecimal.valueOf(29, CoffeeShop.DEFAULT_SCALE); private final Beverage beverage; MilkDecorator(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + " with " + description; } @Override public BigDecimal cost() { return beverage.cost().add(price); } @Override public String toString() { return "MilkDecorator{" + "description='" + description + '\'' + ", price=" + price + ", beverage=" + beverage + '}'; } }
class ChocolateDecorator implements Beverage { private final String description = "Chocolate"; private final BigDecimal price = BigDecimal.valueOf(35, CoffeeShop.DEFAULT_SCALE); private final Beverage beverage; ChocolateDecorator(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + " with " + description; } @Override public BigDecimal cost() { return beverage.cost().add(price); } }
Lets see how we can use this:
class CoffeeShop { public static final int DEFAULT_SCALE = 2; public static void main(String[] args) { //coffee with Milk and Chocolate Beverage coffee = new Coffee(); Beverage coffeeWithMilk = new MilkDecorator(coffee); Beverage coffeeWithMilkAndChocolate = new ChocolateDecorator(coffeeWithMilk); printBeverage(coffeeWithMilkAndChocolate); System.out.println("\n\n"); //Tea with Chocolate Beverage tea = new Tea(); tea = addChocolate(tea); tea = addMilk(tea); printBeverage(tea); } private static Beverage addMilk(Beverage beverage) { return new MilkDecorator(beverage); } private static Beverage addChocolate(Beverage beverage) { return new ChocolateDecorator(beverage); } private static void printBeverage(Beverage beverage) { System.out.println("Your Beverage:\n" + beverage.getDescription() + "\nPrice: " + beverage.cost().setScale(2, BigDecimal.ROUND_HALF_DOWN)); } }
Output:
Your Beverage: Coffee with Milk with Chocolate Price: 1.93 Your Beverage: Tea with Chocolate with Milk Price: 1.63