如何在 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();

我想达到的是

到目前为止我失败了,如果你能帮我找到解决办法,我将不胜感激:) 或者也许我应该考虑一种不同的方法?

我使用的最有价值的资源

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 的构建器接口。
处理 IAgeIName 接口有什么好处?
似乎信息级别太低,对您的构建器没有用。
为什么不简单地声明你这样的基础建设者:

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. 构建器中的参数需要是扩展构建器,而不是要构建的类型。
  2. 公共参数的所有接口都需要使用它进行参数化,以允许适当的延续。

这里有一个建议: 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();