Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorator pattern using Java 8

Wikipedia has an example of a decorator pattern here:

https://en.wikipedia.org/wiki/Decorator_pattern#Second_example_.28coffee_making_scenario.29

I was trying to solve this using functional style using Java 8,the solution I came up:

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 with the solution in the CoffeeIngredientCalculator. If we had a single responsibility in the Coffee interface, getCost(), using the functional style and applying the decorator pattern seems a lot better and cleaner. It would basically boil down to a Function<Double,Double> ,we would not need the abstract class, separate decorators and can just chain the functions.

But in the coffee example, with 2 behaviors of the cost and description on the Coffee object, I am not so convinced that this is a significant value addition as we are creating an anonymous class,overriding the 2 methods.

Questions:

1) Is this solution acceptable ?

2) If not, is there a better way to solve it using functional style?

3) Should we stick to the usual GOF way of having an abstract class and separate decorator classes in scenarios where the object that we are decorating has multiple methods?

like image 911
Ajay Iyengar Avatar asked Jul 28 '16 07:07

Ajay Iyengar


People also ask

What is the decorator pattern in Java?

Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.

What is the decorator pattern used for?

The Decorator Pattern allows class behavior to the decorated dynamically. It's a structural design pattern as it's used to form large object structures across many disparate objects. The concept of decorator is that it adds additional attributes to an object dynamically.

What is the decorator pattern in Java Can you give an example of a decorator pattern?

Decorator design pattern is one of the structural design pattern (such as Adapter Pattern, Bridge Pattern, Composite Pattern) and uses abstract classes or interface with composition to implement.

What is decorator in design patterns?

The decorator design pattern is a structural pattern, which provides a wrapper to the existing class. The decorator design pattern uses abstract classes or interfaces with the composition to implement the wrapper.


2 Answers

So, just quick answers:

  1. Yes, it's acceptable
  2. Though you could decorate / configure the Coffee methods independently. Depends on your requirements. Since Coffee isn't a functional interface, i.e. has more than 1 method, you have to fall back to the plain old subclassing.
  3. No, you don't have to follow GoF literally. They describe more than one alternatives anyway.

Just a final note: If you don't like anonymous classes, then you can write private static inner classes, or whatever. One is more compact, the other plays better with garbage collector.

like image 50
Tamas Rev Avatar answered Oct 21 '22 11:10

Tamas Rev


Your solution is acceptable.

For practical reasons like single implementation of equals, hashCode, toString I would replace anonymous class with

constructor

return new Coffee(coffee.getCost() + 0.2, coffee.getIngredients() + ", Sprinkles");

factory method

return coffee(coffee.getCost() + 0.2, coffee.getIngredients() + ", Sprinkles");

or even copy methods (like in immutables)

return coffee
    .withCost(coffee.getCost() + 0.2) //new instance
    .withIngredients(coffee.getIngredients() + ", Sprinkles"); //another new instance
like image 2
Nazarii Bardiuk Avatar answered Oct 21 '22 11:10

Nazarii Bardiuk