【问题标题】:Decorator pattern using Java 8使用 Java 8 的装饰器模式
【发布时间】:2016-07-28 07:11:06
【问题描述】:

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

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());
}

}

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";
        }
    };
}

}

现在,我不太相信 CoffeeIngredientCalculator 中的解决方案。如果我们在 Coffee 接口中有一个单一的职责,getCost(),使用函数式风格并应用装饰器模式看起来会更好更简洁。它基本上可以归结为 Function&lt;Double,Double&gt; ,我们不需要抽象类、单独的装饰器,只需链接函数即可。

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

问题:

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

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

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

【问题讨论】:

标签: design-patterns functional-programming java-8


【解决方案1】:

所以,请快速回答:

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

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

【讨论】:

  • 感谢您的回答。在超过 1 种方法的情况下,如果我们仍然使用匿名类或私有静态内部类的功能方式,它是不是比 GoF 方式更好的解决方案?我们确实摆脱了 Abstract 装饰器,但我们留下了实际的装饰器(匿名类/静态内部类),它们现在似乎实现了 Coffee 接口。
【解决方案2】:

您的解决方案是可以接受的。

出于实际原因,例如 equalshashCodetoString 的单一实现,我会将匿名类替换为

构造函数

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

工厂方法

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

甚至复制方法(如immutables

return coffee
    .withCost(coffee.getCost() + 0.2) //new instance
    .withIngredients(coffee.getIngredients() + ", Sprinkles"); //another new instance

【讨论】:

  • equals、hashCode 和 toString 是一个很好的观点,但我不确定我是否理解构造函数/工厂方法的解决方案,因为 Coffee 是一个接口而不是一个类。您指的是可能不是 Coffee 实例的装饰器实例吗?你能详细说明一下吗?
  • 在构造函数的情况下,它可以是一个包含成本和成分的咖啡类。工厂方法仍然可以使用接口 - 它应该隐藏对象创建代码并提供更好的代码重用。
猜你喜欢
  • 2013-05-07
  • 2015-05-27
  • 2011-05-19
  • 1970-01-01
  • 1970-01-01
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多