使用 Java 8 的装饰器模式

Aja*_*gar 5 design-patterns functional-programming java-8

维基百科这里有一个装饰器模式的例子:

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

我试图使用 Java 8 使用函数式风格来解决这个问题,我提出的解决方案是:

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());
}
Run Code Online (Sandbox Code Playgroud)

}

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";
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

}

现在,我不太相信 CoffeeIngredientCalculator 中的解决方案。如果我们在 Coffee 接口中有一个单一的职责,getCost(),使用函数式风格和应用装饰器模式似乎更好更干净。它基本上可以归结为一个Function<Double,Double>,我们不需要抽象类,单独的装饰器,只需链接函数即可。

但是在咖啡示例中,在 Coffee 对象上有成本和描述的 2 个行为,我不太相信这是一个重要的增值,因为我们正在创建一个匿名类,覆盖 2 个方法。

问题:

1)这个解决方案可以接受吗?

2)如果没有,有没有更好的方法来使用函数式风格来解决它?

3)在我们装饰的对象有多个方法的情况下,我们是否应该坚持使用抽象类和单独的装饰器类的通常 GOF 方式?

Tam*_*Rev 3

所以,只是快速回答:

  1. 是的,可以接受
  2. 尽管您可以独立装饰/配置这些Coffee方法。取决于您的要求。由于Coffee不是函数式接口,即具有多个方法,因此您必须退回到普通的旧子类化。
  3. 不,您不必严格遵循 GoF。无论如何,他们描述了不止一种替代方案。

最后一点:如果您不喜欢匿名类,那么您可以编写private static内部类或其他类。一种更紧凑,另一种更适合垃圾收集器。