The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.
// Coffee.java
public interface Coffee {
String getDescription();
double getCost();
}
// PlainCoffee.java
public class PlainCoffee implements Coffee {
@Override
public String getDescription() {
return "Plain Coffee";
}
@Override
public double getCost() {
return 2.0;
}
}
// CoffeeDecorator.java
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
}
// MilkDecorator.java
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Milk";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 0.5;
}
}
// SugarDecorator.java
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Sugar";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 0.2;
}
}
// Main.java
public class Main {
public static void main(String[] args) {
// Plain Coffee
Coffee coffee = new PlainCoffee();
System.out.println("Description: " + coffee.getDescription());
System.out.println("Cost: $" + coffee.getCost());
// Coffee with Milk
Coffee milkCoffee = new MilkDecorator(new PlainCoffee());
System.out.println("\nDescription: " + milkCoffee.getDescription());
System.out.println("Cost: $" + milkCoffee.getCost());
// Coffee with Sugar and Milk
Coffee sugarMilkCoffee = new SugarDecorator(new MilkDecorator(new PlainCoffee()));
System.out.println("\nDescription: " + sugarMilkCoffee.getDescription());
System.out.println("Cost: $" + sugarMilkCoffee.getCost());
}
}
As you can see, for each additional property that can be in a cup of coffee, we create a decorator class. This decorator class can alter other attributes of the main object (like cost
and description
). Using this pattern will able us to create extensions and removes the need to alter the original class.