我想创建一个具有流畅构建器的类,这两个构建器都可以继承和扩展。基类应具有所有常用和必填字段,子项应具有不同的可选字段
下面的简单示例(我能想出的最好,简单的用例; 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();
到目前为止,我失败了,如果你能帮我找到解决办法,我将非常感激:) 或者我应该考虑一下不同的方法?
http://blog.crisp.se/2013/10/09/perlundholm/another-builder-pattern-for-java
http://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html
Generic fluent Builder in Java
到目前为止的代码可以在下面找到。有一些我试过和失败的东西的痕迹,有一些未使用或只是奇怪的东西(最好的例子是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);
}
}
答案 0 :(得分:3)
在代码中看起来很多泛型,我试图简化它。
<强>动物强>
package come.stackoverflow.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.stackoverflow.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.stackoverflow.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'}'
答案 1 :(得分:1)
问题出在抽象构建器中:
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();
}
因此,当您调用IBuild<T>
方法时,所有具体构建器都返回相同的age()
接口。
如你所见:
interface IBuild<T extends Animal<T>> {
T build();
}
此接口不允许返回具有使用构建器设置属性的方法的对象。
当您调用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();
}
这样,在编译时,具体构建器将在您调用builder.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();
}
}
我还没有测试过代码。
答案 2 :(得分:1)
您的代码问题是界面:
interface IAge {
IBuild age(Integer age);
}
这将始终返回不带参数的基本IBuild
接口,无论该实现是否使用某些参数来实现。实际上,即使使用参数返回它也不会用其他方法扩展构建器。
这是一个建议:
1.不要使用IName
界面。用构建器的静态输入法替换
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();