Wikipedia has a sample decorator here:
https://en.wikipedia.org/wiki/Decorator_pattern#Second_example_.28coffee_making_scenario.29
I tried to solve this using a functional style using Java 8, the solution I chose:
1.CoffeeDecorator.java
public class CoffeeDecorator {
public static Coffee getCoffee(Coffee basicCoffee, Function<Coffee, Coffee>... coffeeIngredients) {
Function<Coffee, Coffee> chainOfFunctions = Stream.of(coffeeIngredients)
.reduce(Function.identity(),Function::andThen);
return chainOfFunctions.apply(basicCoffee);
}
public static void main(String args[]) {
Coffee simpleCoffee = new SimpleCoffee();
printInfo(simpleCoffee);
Coffee coffeeWithMilk = CoffeeDecorator.getCoffee(simpleCoffee, CoffeeIngredientCalculator::withMilk);
printInfo(coffeeWithMilk);
Coffee coffeeWithWSprinkle = CoffeeDecorator.getCoffee(coffeeWithMilk,CoffeeIngredientCalculator::withSprinkles);
printInfo(coffeeWithWSprinkle);
}
public static void printInfo(Coffee c) {
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
}
}
2.CoffeeIngredientCalculator.java
public class CoffeeIngredientCalculator {
public static Coffee withMilk(Coffee coffee) {
return new Coffee() {
@Override
public double getCost() {
return coffee.getCost() + 0.5;
}
@Override
public String getIngredients() {
return coffee.getIngredients() + " , Milk";
}
};
}
public static Coffee withSprinkles(Coffee coffee) {
return new Coffee() {
@Override
public double getCost() {
return coffee.getCost() + 0.2;
}
@Override
public String getIngredients() {
return coffee.getIngredients() + " , Sprinkles";
}
};
}
}
Now I am not so convinced of the decision in CoffeeIngredientCalculator. If we had one responsibility in the Coffee interface, getCost (), using the functional style and applying the decorator pattern seems much better and cleaner. It would basically come down to Function<Double,Double>, we don’t need an abstract class, separate decorators and just chains of functions.
, 2 Coffee, , , , .
:
1) ?
2) , ?
3) GOF , , , ?