随着Java中lambda的出现,我们现在有了一个新工具,可以更好地设计我们的代码。 当然,第一步是使用流,方法引用和Java 8中引入的其他简洁功能。
展望未来,我认为下一步是重新访问完善的设计模式,并通过功能性编程视角进行了解。 为此,我将采用Decorator模式,并使用lambdas实现它。
我们将以装饰器模式的简单且美味的示例为例:在披萨中添加浇头。 这是GoF建议的标准实现:
首先,我们具有定义组件的接口:
public interface Pizza {
String bakePizza();
}
我们有一个具体的组成部分:
public class BasicPizza implements Pizza {
@Override
public String bakePizza() {
return "Basic Pizza";
}
}
我们决定必须以不同的方式装饰组件。 我们选择装饰器模式。 这是抽象装饰器:
public abstract class PizzaDecorator implements Pizza {
private final Pizza pizza;
protected PizzaDecorator(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String bakePizza() {
return pizza.bakePizza();
}
}
我们为组件提供了一些具体的装饰器:
public class ChickenTikkaPizza extends PizzaDecorator {
protected ChickenTikkaPizza(Pizza pizza) {
super(pizza);
}
@Override
public String bakePizza() {
return super.bakePizza() + " with chicken topping";
}
}
public class ProsciuttoPizza extends PizzaDecorator {
protected ProsciuttoPizza(Pizza pizza) {
super(pizza);
}
@Override
public String bakePizza() {
return super.bakePizza() + " with prosciutto";
}
}
这是使用新结构的方式:
Pizza pizza = new ChickenTikkaPizza(new BasicPizza());
String finishedPizza = pizza.bakePizza(); //Basic Pizza with chicken topping
pizza = new ChickenTikkaPizza(new ProsciuttoPizza(new BasicPizza()));
finishedPizza = pizza.bakePizza(); //Basic Pizza with prosciutto with chicken topping
我们可以看到这会变得非常混乱,如果考虑如何处理Java中的缓冲读取器,它的确会变得非常混乱:
new DataInputStream(new BufferedInputStream(new FileInputStream(new File("myfile.txt"))))
当然,您可以将其拆分为多行,但这不会解决混乱问题,只会分散它。 现在让我们看看如何使用lambda来做同样的事情。 我们从相同的基本组件对象开始:
public interface Pizza {
String bakePizza();
}
public class BasicPizza implements Pizza {
@Override
public String bakePizza() {
return "Basic Pizza";
}
}
但是现在,我们不再声明一个抽象类来提供装饰的模板,而是创建一个装饰器,该装饰器要求用户提供装饰组件的功能。
public class PizzaDecorator {
private final Function<Pizza, Pizza> toppings;
private PizzaDecorator(Function<Pizza, Pizza>... desiredToppings) {
this.toppings = Stream.of(desiredToppings)
.reduce(Function.identity(), Function::andThen);
}
public static String bakePizza(Pizza pizza, Function<Pizza, Pizza>... desiredToppings) {
return new PizzaDecorator(desiredToppings).bakePizza(pizza);
}
private String bakePizza(Pizza pizza) {
return this.toppings.apply(pizza).bakePizza();
}
}
这条线构成了要应用的装饰链:
Stream.of(desiredToppings).reduce(identity(), Function::andThen);
这行代码将接受您的装饰(属于Function类型),并使用andThen链接它们。 这和
(currentToppings, nextTopping) -> currentToppings.andThen(nextTopping)
并确保随后按您提供的顺序调用这些函数。 还将Function.identity()转换为elem-> elem lambda表达式。 好的,现在我们在哪里定义装饰? 您可以在PizzaDecorator甚至在界面中将它们添加为静态方法:
public interface Pizza {
String bakePizza();
static Pizza withChickenTikka(Pizza pizza) {
return new Pizza() {
@Override
public String bakePizza() {
return pizza.bakePizza() + " with chicken";
}
};
}
static Pizza withProsciutto(Pizza pizza) {
return new Pizza() {
@Override
public String bakePizza() {
return pizza.bakePizza() + " with prosciutto";
}
};
}
}
现在,这就是这种模式的使用方式:
String finishedPizza = PizzaDecorator.bakePizza(new BasicPizza(),Pizza::withChickenTikka, Pizza::withProsciutto);
//And if you static import PizzaDecorator.bakePizza:
String finishedPizza = bakePizza(new BasicPizza(),Pizza::withChickenTikka, Pizza::withProsciutto);
如您所见,代码变得更加清晰和简洁,我们没有使用继承来构建我们的装饰器。
这只是可以使用lambda改进的众多设计模式之一。 还有更多功能可用来改善其余功能,例如使用部分应用程序(循环)来实现适配器模式。
希望我能引起您考虑对您的开发风格采用功能更强大的编程方法的想法。
参考书目
装饰器示例的灵感来自“ 四人帮” –“用装饰器设计模式进行装饰”一文。
重构方法是受以下Devoxx 2015演讲的启发(我建议在观看这些演讲时将其视为主题): Remi Forax重新加载的 设计模式,Venkat Subramaniam的Lambda表达形式的设计模式
翻译自: https://www.javacodegeeks.com/2015/12/decorator-design-pattern-using-lambdas.html