如何在Java8中实现构建器模式?
在Java8之前实现构建器模式有很多乏味的、几乎重复的代码;构建器本身通常是样板代码。有些人把java 8的前一个建造者的每一个方法都看作是其他方法的复制品。 考虑以下前Java 8构建器模式:如何在Java8中实现构建器模式?,java,java-8,builder,Java,Java 8,Builder,在Java8之前实现构建器模式有很多乏味的、几乎重复的代码;构建器本身通常是样板代码。有些人把java 8的前一个建造者的每一个方法都看作是其他方法的复制品。 考虑以下前Java 8构建器模式: 公共类人物{ 私有字符串名称; 私人互联网; 公共字符串getName(){ 返回名称; } 公共void集合名(字符串名){ this.name=名称; } 公共整数getAge(){ 回归年龄; } 公共无效设置(整数){ 这个。年龄=年龄; } } 公共类人员生成器{ 私有静态类PersonSta
公共类人物{
私有字符串名称;
私人互联网;
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
公共整数getAge(){
回归年龄;
}
公共无效设置(整数){
这个。年龄=年龄;
}
}
公共类人员生成器{
私有静态类PersonState{
公共字符串名称;
公共信息;
}
私有PersonState=新PersonState();
带名称的公共PersonBuilder(字符串名称){
state.name=名称;
归还这个;
}
公共人员建筑商withAge(int age){
state.age=年龄;
归还这个;
}
公众人物塑造(){
Person=新人();
人.年龄(州.年龄);
person.setName(state.name);
状态=新的PersonState();
返回人;
}
}
如何使用Java 8实现生成器模式?GenericBuilder
构建可变对象的思想是使用方法引用来设置应该构建的实例。这使我们找到了一个通用的构建器,它能够使用默认的构造函数构建每个POJO—一个构建器来管理所有POJO;-)
具体实施如下:
public class GenericBuilder<T> {
private final Supplier<T> instantiator;
private List<Consumer<T>> instanceModifiers = new ArrayList<>();
public GenericBuilder(Supplier<T> instantiator) {
this.instantiator = instantiator;
}
public static <T> GenericBuilder<T> of(Supplier<T> instantiator) {
return new GenericBuilder<T>(instantiator);
}
public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value) {
Consumer<T> c = instance -> consumer.accept(instance, value);
instanceModifiers.add(c);
return this;
}
public T build() {
T value = instantiator.get();
instanceModifiers.forEach(modifier -> modifier.accept(value));
instanceModifiers.clear();
return value;
}
}
性质和进一步用途
但还有更多关于建设者的事情需要去发现
例如,上面的实现清除修饰符。这可以转化为它自己的方法。因此,构建器将在修改之间保持其状态,并且很容易创建多个相等的实例。或者,根据实例修改器
的性质,不同对象的列表。例如,instanceModifier
可以从递增计数器读取其值
继续这个想法,我们可以实现一个fork
方法,该方法将返回调用它的GenericBuilder
实例的新克隆。这很容易实现,因为构建器的状态只是实例化器和实例修改器列表。从那时起,两个构建器都可以用其他一些实例修改器来修改。它们将共享相同的基础,并在构建实例上设置一些额外的状态
最后一点,我认为在企业应用程序中需要重实体进行单元甚至集成测试时特别有用。对于实体来说,没有上帝的客体,而是对于建设者
GenericBuilder
还可以取代对不同测试值工厂的需求。在我当前的项目中,有许多工厂用于创建测试实例。代码与不同的测试场景紧密耦合,在稍有不同的场景中,很难提取测试工厂的部分以在另一个测试工厂中重用。使用GenericBuilder
,重用它变得更加容易,因为只有一个特定的实例修改器列表
要验证创建的实例是否有效,可以使用一组谓词初始化GenericBuilder
,在运行所有instancemodifier
后,在build
方法中验证这些谓词
public T build() {
T value = instantiator.get();
instanceModifiers.forEach(modifier -> modifier.accept(value));
verifyPredicates(value);
instanceModifiers.clear();
return value;
}
private void verifyPredicates(T value) {
List<Predicate<T>> violated = predicates.stream()
.filter(e -> !e.test(value)).collect(Collectors.toList());
if (!violated.isEmpty()) {
throw new IllegalStateException(value.toString()
+ " violates predicates " + violated);
}
}
publictbuild(){
T value=instantiator.get();
instanceModifiers.forEach(修饰符->修饰符.accept(值));
验证谓词(值);
instanceModifiers.clear();
返回值;
}
私有void verifyPredicates(T值){
List=predicates.stream()
.filter(e->!e.test(value)).collect(collector.toList());
如果(!违犯了.isEmpty()){
抛出新的IllegalStateException(value.toString()
+“违反谓词”+违反谓词);
}
}
不可变对象创建
要使用上述方案创建不可变对象,请将不可变对象的状态提取到可变对象中,并使用实例化器和生成器对可变状态对象进行操作。然后,添加一个函数,该函数将为可变状态创建一个新的不可变实例。但是,这要求不可变对象要么像这样封装其状态,要么以这种方式更改它(基本上是将参数对象模式应用于其构造函数)
这在某种程度上不同于java-8之前使用的构建器。在那里,构建器本身就是最终创建新实例的可变对象。现在,我们将构建器在可变对象中保持的状态与构建器功能本身分离
本质上
停止编写样板构建器模式,使用GenericBuilder
提高效率。您可以检查
为了你的案子
@Builder
public class Person {
private String name;
private int age;
}
它将动态生成代码
public class Person {
private String name;
private int age;
public String getName(){...}
public void setName(String name){...}
public int getAge(){...}
public void setAge(int age){...}
public Person.Builder builder() {...}
public static class Builder {
public Builder withName(String name){...}
public Builder withAge(int age){...}
public Person build(){...}
}
}
Lombok是在编译阶段完成的,对开发人员来说是透明的。我们可以使用Java 8的消费者功能接口来避免使用多个getter/setter方法
请参阅以下带有使用者界面的更新代码
import java.util.function.Consumer;
public class Person {
private String name;
private int age;
public Person(Builder Builder) {
this.name = Builder.name;
this.age = Builder.age;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Person{");
sb.append("name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append('}');
return sb.toString();
}
public static class Builder {
public String name;
public int age;
public Builder with(Consumer<Builder> function) {
function.accept(this);
return this;
}
public Person build() {
return new Person(this);
}
}
public static void main(String[] args) {
Person user = new Person.Builder().with(userData -> {
userData.name = "test";
userData.age = 77;
}).build();
System.out.println(user);
}
}
import java.util.function.Consumer;
公共阶层人士{
私有字符串名称;
私人互联网;
公众人士(建筑商){
this.name=Builder.name;
this.age=Builder.age;
}
@凌驾
公共字符串toString(){
最终StringBuilder sb=新StringBuilder(“人员{”);
sb.append(“name=”).append(name).append(“\”);
某人追加(“,age=”)。追加(age);
某人附加('}');
使某人返回字符串();
}
公共静态类生成器{
import java.util.function.Consumer;
public class Person {
private String name;
private int age;
public Person(Builder Builder) {
this.name = Builder.name;
this.age = Builder.age;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Person{");
sb.append("name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append('}');
return sb.toString();
}
public static class Builder {
public String name;
public int age;
public Builder with(Consumer<Builder> function) {
function.accept(this);
return this;
}
public Person build() {
return new Person(this);
}
}
public static void main(String[] args) {
Person user = new Person.Builder().with(userData -> {
userData.name = "test";
userData.age = 77;
}).build();
System.out.println(user);
}
}
public class PersonBuilder {
public String salutation;
public String firstName;
public String middleName;
public String lastName;
public String suffix;
public Address address;
public boolean isFemale;
public boolean isEmployed;
public boolean isHomewOwner;
public PersonBuilder with(
Consumer<PersonBuilder> builderFunction) {
builderFunction.accept(this);
return this;
}
public Person createPerson() {
return new Person(salutation, firstName, middleName,
lastName, suffix, address, isFemale,
isEmployed, isHomewOwner);
}
}
Person person = new PersonBuilder()
.with($ -> {
$.salutation = "Mr.";
$.firstName = "John";
$.lastName = "Doe";
$.isFemale = false;
})
.with($ -> $.isHomewOwner = true)
.with($ -> {
$.address =
new PersonBuilder.AddressBuilder()
.with($_address -> {
$_address.city = "Pune";
$_address.state = "MH";
$_address.pin = "411001";
}).createAddress();
})
.createPerson();
public class Person {
static public Person create(Consumer<PersonBuilder> buildingFunction) {
return new Person().build(buildingFunction);
}
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
private Person() {
}
private Person build(Consumer<PersonBuilder> buildingFunction) {
buildingFunction.accept(new PersonBuilder() {
@Override
public PersonBuilder withName(String name) {
Person.this.name = name;
return this;
}
@Override
public PersonBuilder withAge(int age) {
Person.this.age = age;
return this;
}
});
if (name == null || name.isEmpty()) {
throw new IllegalStateException("the name must not be null or empty");
}
if (age <= 0) {
throw new IllegalStateException("the age must be > 0");
}
// check other invariants
return this;
}
}
public interface PersonBuilder {
PersonBuilder withName(String name);
PersonBuilder withAge(int age);
}
var person = Person.create(
personBuilder -> personBuilder.withName("John Smith").withAge(43)
);
public class Person {
static public Person create(Consumer<PersonBuilder> buildingFunction) {
return new Person(buildingFunction);
}
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
private Person(Consumer<PersonBuilder> buildingFunction) {
buildingFunction.accept(new PersonBuilder() {
@Override
public PersonBuilder withName(String name) {
Person.this.name = name;
return this;
}
@Override
public PersonBuilder withAge(int age) {
Person.this.age = age;
return this;
}
});
if (name == null || name.isEmpty()) {
throw new IllegalStateException("the name must not be null or empty");
}
if (age <= 0) {
throw new IllegalStateException("the age must be > 0");
}
// check other invariants
}
}