如何在 java 中实现带有继承的 Fluent Builder
how to implement fluent builder with inheritance in java
问题
我想创建一个class有fluent builder的,两者都可以继承和扩展。 Base class 应该有所有公共和必填字段,children 应该有不同的可选字段
下面的简单示例(我能想到的最简单的用例;p)
base: Animal
name
age
static Builder
impl: Snake extends Animal
length
static Builder extends Animal.Builder
impl: Spider extends Animal
numberOfLegs
static Builder extends Animal.Builder
我想以其中一种方式使用它(最喜欢的是第一种):
Spider elvis = Spider.name("elvis").age(1).numberOfLegs(8).build();
Spider elvis = Spider.builder().name("elvis").age(1).numberOfLegs(8).build();
Spider elvis = new Spider.Builder().name("elvis").age(1).numberOfLegs(8).build();
我想达到的是
- 此构建器的用户必须提供一些最少的信息(这样系统才能正常工作),否则他将无法构建 object
- 所有可选字段都可以在必填字段之后声明,没有特定顺序
- 我可能需要为 children 添加一些必填字段,但只需更改构建器中调用的第一个方法即可轻松处理
- 我不想在那些 classes 之外进行任何转换(此处:在 Main 中),但我不介意在这段代码中(此处:在 Animal 或 Spider 中)
到目前为止我失败了,如果你能帮我找到解决办法,我将不胜感激:)
或者也许我应该考虑一种不同的方法?
我使用的最有价值的资源
http://blog.crisp.se/2013/10/09/perlundholm/another-builder-pattern-for-java
http://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html
到目前为止完成的工作
到目前为止的代码可以在下面找到。有一些我尝试和失败的痕迹,有一些未使用或只是奇怪的东西(最好的例子是 IBuildImpl)。留下这些是为了让您了解我的尝试,但如果您认为这需要适度 - 请告诉我,我会清理它们
基地
package fafafa;
public abstract class Animal<T> {
String name; //mandatory field, one of many
Integer age; //mandatory field, one of many
public String getName() {
return name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
interface IName {
IAge name(String name);
}
interface IAge {
IBuild age(Integer age);
}
interface IBuild<T extends Animal<T>> {
T build();
}
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>
implements IName, IAge, IBuild<T> {
protected T objectBeingBuilt;
protected abstract B that();
protected abstract T createEmptyObject();
Builder(){
this.objectBeingBuilt = createEmptyObject();
System.out.println();
}
@Override
public IAge name(String name) {
objectBeingBuilt.name = name;
return that();
}
@Override
public IBuild age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
// @Override
// public T build() {
// return objectBeingBuilt;
// }
}
}
实现
package fafafa;
public class Spider extends Animal<Spider> {
Integer numberOfLegs; //optional field, one of many
private Spider() {
}
public Integer getNumberOfLegs() {
return numberOfLegs;
}
@Override
public String toString() {
return "Spider{" +
"numberOfLegs='" + numberOfLegs + '\'' +
"} " + super.toString();
}
// public static Builder<Spider, Builder> name(String name) {
// return (Builder) new Builder().name(name);
// }
interface INumberOfLegs {
IBuild numberOfLegs(Integer numberOfLegs);
}
interface IBuildImpl extends IBuild<Spider>, INumberOfLegs {
@Override
Spider build();
}
public static class Builder extends Animal.Builder<Spider, Builder> implements IBuildImpl {
@Override
protected Builder that() {
return this;
}
@Override
protected Spider createEmptyObject() {
return new Spider();
}
public IBuild numberOfLegs(Integer numberOfLegs) {
objectBeingBuilt.numberOfLegs = numberOfLegs;
return that();
}
public Spider build() {
return objectBeingBuilt;
}
}
}
主要
package fafafa;
public class Main {
public static void main(String[] args) {
Spider build = new Spider.Builder().name("elvis")
.age(1)
.numberOfLegs(8) //cannot resolve method numberOfLegs
.build();
System.out.println(build);
}
}
问题出在抽象构建器中:
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>
implements IName, IAge, IBuild<T> {
...
@Override
public IAge name(String name) {
objectBeingBuilt.name = name;
return that();
}
@Override
public IBuild age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
因此,当您调用 age()
方法时,您的所有具体构建器 return 具有相同的 IBuild<T>
界面。
如您所见:
interface IBuild<T extends Animal<T>> {
T build();
}
此接口不允许 return 对象,您可以在其中使用构建器设置属性。
当你调用 name()
方法时,你也没有得到构建器 :
interface IAge {
IBuild age(Integer age);
}
你应该像这样在抽象构建器中声明 age()
和 name()
:
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>{
...
public B name(String name) {
objectBeingBuilt.name = name;
return that();
}
public B age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
这样,在编译时,具体构建器将return您正在创建的动物的构建器,当您调用builder.age(..)
.
此外,我不明白为什么有一个用于 name 的构建器接口和另一个用于 age 的构建器接口。
处理 IAge
和 IName
接口有什么好处?
似乎信息级别太低,对您的构建器没有用。
为什么不简单地声明你这样的基础建设者:
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>
implements IBuild<T> {
protected T objectBeingBuilt;
protected abstract B that();
protected abstract T createEmptyObject();
Builder(){
this.objectBeingBuilt = createEmptyObject();
System.out.println();
}
public B name(String name) {
objectBeingBuilt.name = name;
return that();
}
public B age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
}
我没有测试代码。
看起来像代码中的许多泛型,我试着稍微简化一下。
动物
package come.Whosebug.builder;
public abstract class Animal {
private final String name; //mandatory field, one of many
private final Integer age; //mandatory field, one of many
Animal(final String name, final Integer age) {this.name = name; this.age = age;}
public String getName() {return name;}
public Integer getAge() {return age;}
@Override public String toString() {return String.format("Animal {name='%s', age='%s'}'", name, age);}
interface IBuild<T> {
T build();
}
public abstract static class AnimalBuilder<B extends AnimalBuilder, T extends Animal> implements IBuild<T> {
String name;
Integer age;
public B name(final String name) {this.name = name; return (B) this;}
public B age(final Integer age) {this.age = age; return (B) this;}
}
}
蜘蛛
package come.Whosebug.builder;
public class Spider extends Animal {
private final Integer numberOfLegs; //optional field, one of many
private Spider(final String name, final Integer age, final Integer numberOfLegs) {super(name, age); this.numberOfLegs = numberOfLegs;}
public Integer getNumberOfLegs() {return numberOfLegs;}
@Override public String toString() {return String.format("Spider {numberOfLegs='%s'}, %s", getNumberOfLegs(), super.toString());}
public static class SpiderBuilder extends AnimalBuilder<SpiderBuilder, Spider> {
Integer numberOfLegs;
public SpiderBuilder numberOfLegs(final Integer numberOfLegs) {this.numberOfLegs = numberOfLegs; return this;}
public Spider build() {return new Spider(name, age, numberOfLegs);}
}
}
主要测试
import come.Whosebug.builder.Spider;
public class Main {
public static void main(String[] args) {
Spider build = new Spider.SpiderBuilder()
.name("elvis").numberOfLegs(8).age(1)
.build();
System.out.println(build);
}
}
执行结果:
Spider {numberOfLegs='8'}, Animal {name='elvis', age='1'}'
你的代码的问题是接口:
interface IAge {
IBuild age(Integer age);
}
这将始终 return 没有参数的基本 IBuild
接口,无论如何,如果实现使用一些参数来实现它。实际上,即使 return 使用参数对其进行处理也不会使用其他方法扩展构建器。
- 构建器中的参数需要是扩展构建器,而不是要构建的类型。
- 公共参数的所有接口都需要使用它进行参数化,以允许适当的延续。
这里有一个建议:
1.不要使用IName
接口。替换成builder的静态入口方法
2.参数化IAge
接口
3.不需要普通的建造者。它可以替换为内联 lambda 实现
代码如下:
@FunctionalInterface
public interface IAge<B> {
B age(Integer age);
}
public class AnimalBuilder implements IBuild<Animal> {
private final String name;
private final Integer age;
private Integer numberOfLegs;
private AnimalBuilder(String name, Integer age) {
this.name = name;
this.age = age;
}
// Builder entry method
public static IAge<AnimalBuilder> name(String name) {
return age -> new AnimalBuilder(name, age);
}
public AnimalBuilder numberOfLegs(int value) {
numberOfLegs = value;
return this;
}
@Override
public Animal build() {
return new Animal(name, age, numberOfLegs);
}
}
这允许以下用法:
AnimalBuilder.name("elvis").age(1).numberOfLegs(8).build();
问题
我想创建一个class有fluent builder的,两者都可以继承和扩展。 Base class 应该有所有公共和必填字段,children 应该有不同的可选字段
下面的简单示例(我能想到的最简单的用例;p)
base: Animal
name
age
static Builder
impl: Snake extends Animal
length
static Builder extends Animal.Builder
impl: Spider extends Animal
numberOfLegs
static Builder extends Animal.Builder
我想以其中一种方式使用它(最喜欢的是第一种):
Spider elvis = Spider.name("elvis").age(1).numberOfLegs(8).build();
Spider elvis = Spider.builder().name("elvis").age(1).numberOfLegs(8).build();
Spider elvis = new Spider.Builder().name("elvis").age(1).numberOfLegs(8).build();
我想达到的是
- 此构建器的用户必须提供一些最少的信息(这样系统才能正常工作),否则他将无法构建 object
- 所有可选字段都可以在必填字段之后声明,没有特定顺序
- 我可能需要为 children 添加一些必填字段,但只需更改构建器中调用的第一个方法即可轻松处理
- 我不想在那些 classes 之外进行任何转换(此处:在 Main 中),但我不介意在这段代码中(此处:在 Animal 或 Spider 中)
到目前为止我失败了,如果你能帮我找到解决办法,我将不胜感激:) 或者也许我应该考虑一种不同的方法?
我使用的最有价值的资源
http://blog.crisp.se/2013/10/09/perlundholm/another-builder-pattern-for-java
http://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html
到目前为止完成的工作
到目前为止的代码可以在下面找到。有一些我尝试和失败的痕迹,有一些未使用或只是奇怪的东西(最好的例子是 IBuildImpl)。留下这些是为了让您了解我的尝试,但如果您认为这需要适度 - 请告诉我,我会清理它们
基地
package fafafa;
public abstract class Animal<T> {
String name; //mandatory field, one of many
Integer age; //mandatory field, one of many
public String getName() {
return name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
interface IName {
IAge name(String name);
}
interface IAge {
IBuild age(Integer age);
}
interface IBuild<T extends Animal<T>> {
T build();
}
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>
implements IName, IAge, IBuild<T> {
protected T objectBeingBuilt;
protected abstract B that();
protected abstract T createEmptyObject();
Builder(){
this.objectBeingBuilt = createEmptyObject();
System.out.println();
}
@Override
public IAge name(String name) {
objectBeingBuilt.name = name;
return that();
}
@Override
public IBuild age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
// @Override
// public T build() {
// return objectBeingBuilt;
// }
}
}
实现
package fafafa;
public class Spider extends Animal<Spider> {
Integer numberOfLegs; //optional field, one of many
private Spider() {
}
public Integer getNumberOfLegs() {
return numberOfLegs;
}
@Override
public String toString() {
return "Spider{" +
"numberOfLegs='" + numberOfLegs + '\'' +
"} " + super.toString();
}
// public static Builder<Spider, Builder> name(String name) {
// return (Builder) new Builder().name(name);
// }
interface INumberOfLegs {
IBuild numberOfLegs(Integer numberOfLegs);
}
interface IBuildImpl extends IBuild<Spider>, INumberOfLegs {
@Override
Spider build();
}
public static class Builder extends Animal.Builder<Spider, Builder> implements IBuildImpl {
@Override
protected Builder that() {
return this;
}
@Override
protected Spider createEmptyObject() {
return new Spider();
}
public IBuild numberOfLegs(Integer numberOfLegs) {
objectBeingBuilt.numberOfLegs = numberOfLegs;
return that();
}
public Spider build() {
return objectBeingBuilt;
}
}
}
主要
package fafafa;
public class Main {
public static void main(String[] args) {
Spider build = new Spider.Builder().name("elvis")
.age(1)
.numberOfLegs(8) //cannot resolve method numberOfLegs
.build();
System.out.println(build);
}
}
问题出在抽象构建器中:
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>
implements IName, IAge, IBuild<T> {
...
@Override
public IAge name(String name) {
objectBeingBuilt.name = name;
return that();
}
@Override
public IBuild age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
因此,当您调用 age()
方法时,您的所有具体构建器 return 具有相同的 IBuild<T>
界面。
如您所见:
interface IBuild<T extends Animal<T>> {
T build();
}
此接口不允许 return 对象,您可以在其中使用构建器设置属性。
当你调用 name()
方法时,你也没有得到构建器 :
interface IAge {
IBuild age(Integer age);
}
你应该像这样在抽象构建器中声明 age()
和 name()
:
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>{
...
public B name(String name) {
objectBeingBuilt.name = name;
return that();
}
public B age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
这样,在编译时,具体构建器将return您正在创建的动物的构建器,当您调用builder.age(..)
.
此外,我不明白为什么有一个用于 name 的构建器接口和另一个用于 age 的构建器接口。
处理 IAge
和 IName
接口有什么好处?
似乎信息级别太低,对您的构建器没有用。
为什么不简单地声明你这样的基础建设者:
public abstract static class Builder<T extends Animal<T>, B extends Builder<T, B>>
implements IBuild<T> {
protected T objectBeingBuilt;
protected abstract B that();
protected abstract T createEmptyObject();
Builder(){
this.objectBeingBuilt = createEmptyObject();
System.out.println();
}
public B name(String name) {
objectBeingBuilt.name = name;
return that();
}
public B age(Integer age) {
objectBeingBuilt.age = age;
return that();
}
}
我没有测试代码。
看起来像代码中的许多泛型,我试着稍微简化一下。
动物
package come.Whosebug.builder;
public abstract class Animal {
private final String name; //mandatory field, one of many
private final Integer age; //mandatory field, one of many
Animal(final String name, final Integer age) {this.name = name; this.age = age;}
public String getName() {return name;}
public Integer getAge() {return age;}
@Override public String toString() {return String.format("Animal {name='%s', age='%s'}'", name, age);}
interface IBuild<T> {
T build();
}
public abstract static class AnimalBuilder<B extends AnimalBuilder, T extends Animal> implements IBuild<T> {
String name;
Integer age;
public B name(final String name) {this.name = name; return (B) this;}
public B age(final Integer age) {this.age = age; return (B) this;}
}
}
蜘蛛
package come.Whosebug.builder;
public class Spider extends Animal {
private final Integer numberOfLegs; //optional field, one of many
private Spider(final String name, final Integer age, final Integer numberOfLegs) {super(name, age); this.numberOfLegs = numberOfLegs;}
public Integer getNumberOfLegs() {return numberOfLegs;}
@Override public String toString() {return String.format("Spider {numberOfLegs='%s'}, %s", getNumberOfLegs(), super.toString());}
public static class SpiderBuilder extends AnimalBuilder<SpiderBuilder, Spider> {
Integer numberOfLegs;
public SpiderBuilder numberOfLegs(final Integer numberOfLegs) {this.numberOfLegs = numberOfLegs; return this;}
public Spider build() {return new Spider(name, age, numberOfLegs);}
}
}
主要测试
import come.Whosebug.builder.Spider;
public class Main {
public static void main(String[] args) {
Spider build = new Spider.SpiderBuilder()
.name("elvis").numberOfLegs(8).age(1)
.build();
System.out.println(build);
}
}
执行结果:
Spider {numberOfLegs='8'}, Animal {name='elvis', age='1'}'
你的代码的问题是接口:
interface IAge {
IBuild age(Integer age);
}
这将始终 return 没有参数的基本 IBuild
接口,无论如何,如果实现使用一些参数来实现它。实际上,即使 return 使用参数对其进行处理也不会使用其他方法扩展构建器。
- 构建器中的参数需要是扩展构建器,而不是要构建的类型。
- 公共参数的所有接口都需要使用它进行参数化,以允许适当的延续。
这里有一个建议:
1.不要使用IName
接口。替换成builder的静态入口方法
2.参数化IAge
接口
3.不需要普通的建造者。它可以替换为内联 lambda 实现
代码如下:
@FunctionalInterface
public interface IAge<B> {
B age(Integer age);
}
public class AnimalBuilder implements IBuild<Animal> {
private final String name;
private final Integer age;
private Integer numberOfLegs;
private AnimalBuilder(String name, Integer age) {
this.name = name;
this.age = age;
}
// Builder entry method
public static IAge<AnimalBuilder> name(String name) {
return age -> new AnimalBuilder(name, age);
}
public AnimalBuilder numberOfLegs(int value) {
numberOfLegs = value;
return this;
}
@Override
public Animal build() {
return new Animal(name, age, numberOfLegs);
}
}
这允许以下用法:
AnimalBuilder.name("elvis").age(1).numberOfLegs(8).build();