工厂模式
抽象工厂模式提供了一种封装一组具有共同主题的单个工厂而无需指定其具体类的方法。 在正常使用中,将创建抽象工厂的具体实现,然后使用工厂的通用接口来创建具体对象。
Java领域的(抽象)工厂模式是众所周知的(有时是臭名昭著的) 。 臭名昭著,因为名字倾向于……有时会增长。 糟糕的Spring框架通常只是因为拥有一个名为SimpleBeanFactoryAwareAspectInstanceFactory的类而成为千篇一律的笑话的目标,即使名称实际上选择得当:您将获得一个通过使用另一个工厂来生成方面实例的工厂。
我的示例:想象一个简单的公式来计算从1到n的自然数之和,并将它们表示为功能接口 :
@FunctionalInterface
public interface Computation {
public long sum1To(long n);
}
我们的抽象工厂应该能够提供天真的迭代实现以及著名的GaußscheSummenformel 。 您可以将这些工厂实现为公式或计算的提供者: Supplier<Computation>
,这就是实现多个工厂提供抽象产品的多种不同实现所需要的:
import java.util.function.Supplier;
public class ComputationFactoryClient {
public static void main(final String... a) {
final Supplier<Computation> slowFactory = () -> n -> {
long rv = 0L;
for (long i = 1; i <= n; ++i) {
rv += i;
}
return rv;
};
final Supplier<Computation> gaussFactory = () -> n -> (n * (n+1))/2;
System.out.println(slowFactory.get().sum1To(100));
System.out.println(gaussFactory.get().sum1To(100));
}
}
以这种方式使用工厂模式将自动为您的代码库带来很多灵活性,而不会牺牲可读性。
但是,如何命名这些工厂的问题仍然存在。
建造者模式
构建器模式将复杂对象的构造与其表示分开,以便同一构造过程可以创建不同的表示。
生成器通常与流利的API或小型领域特定语言(DSL)结合使用,以创建不可变对象,而无需“伸缩”构造器(当对象构造器参数组合的增加导致重载的构造器呈指数级列表时,将出现伸缩构造器)。
以下是Spring Security配置的示例:
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(
"/api/system/env/java.(runtime|vm).*",
"/api/system/metrics/**"
).permitAll()
.antMatchers("/api/system/env/**").denyAll()
.antMatchers("/**").permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(STATELESS);
这只是其中的一部分...为构造HTTP安全对象的所有组合而构造的构造函数列表。
构建器模式通常用于配置事物。 配置通常取决于明确定义的步骤顺序。 对我来说,构建器的理想实现包括可读的API,该API遵循步骤的顺序并导致对象不变。 另外,所使用的构建器应仅用于一种目的。
我的示例:三明治的配置。 这个例子不是新的。 Marco Castigliego在2012年就已经使用它来展示他的Step Builder模式,但是正如我在与妻子的讨论中经常想知道的是,肉是属于奶酪的下方还是顶部,它仍然很重要。
要配置以下对象:
import java.util.List;
public class Sandwich {
private final String bread;
private final String meat;
private final String cheese;
private final List<String> vegetables;
public String getBread() {
return bread;
}
public String getMeat() {
return meat;
}
public String getCheese() {
return cheese;
}
public List<String> getVegetables() {
return vegetables;
}
}
为了简洁起见,我将跳过面包和肉的自定义类型。 三明治应使用以下步骤制作:
public static interface ChooseBreadStep {
public ChooseMeatStep withMeat(final String meat);
public AddVeggiesStep vegan();
}
public static interface ChooseMeatStep {
public ChooseCheeseStep withCheese(final String cheese);
public AddVeggiesStep noCheese();
}
public static interface ChooseCheeseStep {
public AddVeggiesStep addVeggie(final String vegetable);
public CloseStep noVeggies();
}
public static interface AddVeggiesStep {
public AddVeggiesStep addVeggie(final String vegetable);
public CloseStep close();
}
public static interface CloseStep {
public Sandwich create();
}
选择面包后,客户可以选择肉或素食三明治。 素食三明治当然也可以跳过奶酪,但是就像肉三明治一样使用蔬菜。 如果您不喜欢蔬菜,也可以跳过。
构建器本身看起来像这样:
private static class Builder implements ChooseBreadStep, ChooseMeatStep, ChooseCheeseStep, AddVeggiesStep, CloseStep {
final String bread;
String meat;
String cheese;
final List<String> vegetables = new ArrayList<>();
Builder(String bread) {
this.bread = bread;
}
@Override
public ChooseMeatStep withMeat(String meat) {
this.meat = meat;
return this;
}
@Override
public AddVeggiesStep vegan() {
return this;
}
@Override
public ChooseCheeseStep withCheese(String cheese) {
this.cheese = cheese;
return this;
}
@Override
public AddVeggiesStep noCheese() {
return this;
}
@Override
public AddVeggiesStep addVeggie(String vegetable) {
this.vegetables.add(vegetable);
return this;
}
@Override
public CloseStep noVeggies() {
return this;
}
@Override
public CloseStep close() {
return this;
}
@Override
public Sandwich create() {
return new Sandwich(this);
}
}
通过三明治类中的私有构造函数实例化三明治:
private Sandwich(Builder builder) {
this.bread = builder.bread;
this.meat = builder.meat;
this.cheese = builder.cheese;
this.vegetables = Collections.unmodifiableList(builder.vegetables);
}
我更喜欢将构建器传递给正在构建的对象,而不是使用空的私有构造器。
到目前为止还没有Java 8。 但是,等等,构建器直到现在还没有初始化。 这将通过类本身的静态方法来完成。 该方法还将采用所有必填参数:
public static Sandwich make(String bread, Function<ChooseBreadStep, CloseStep> configuration) {
return configuration.andThen(CloseStep::create).apply(new Builder(bread));
}
看一下第二个参数:这是一个将ChooseBreadStep
映射到ChooseBreadStep
的CloseStep
。 通过使用函数(或者,如果您不使用步骤,那么使用Consumer
也可以),客户端不能在实际创建对象的上下文之外使用构建器。 该构建器没有公共构造函数,此外,该块结束后将无法重用。
用法比看起来像这样:
public static void main(final String...a) {
Sandwich.make("white", cfg -> cfg
.withMeat("parma")
.withCheese("cheedar")
.addVeggie("salad")
.close()
);
Sandwich.make("brown", cfg -> cfg
.withMeat("salami")
.noCheese()
.close());
Sandwich.make("spelt", cfg -> cfg
.vegan()
.addVeggie("salad")
.addVeggie("gherkins")
.addVeggie("tomatoes")
.close()
);
}
在这种情况下,您可以利用Java 8语言功能来创建一个不太容易出错的API,该API会在创建对象的过程中指导您的客户端。
摘要
函数式编程使许多模式更易于理解和使用。 Lambda或代码块比Java的语法糖要多得多,并且比普通的面向对象编程要分散注意力。
为了进一步阅读,我推荐Venkat Subramaniam博士的演讲“使用Java中的Lambda表达式进行设计” ,这是非常令人鼓舞的资料,还有Mario Fusco的 Github存储库 “从四种模式到Lambdas”。
翻译自: https://jaxenter.com/patterns-java-8-lambdas-127635.html