如何在java中实现具有继承的流畅构建器

时间:2016-12-08 19:40:51

标签: java inheritance builder fluent fluent-interface

问题

我想创建一个具有流畅构建器的类,这两个构建器都可以继承和扩展。基类应具有所有常用和必填字段,子项应具有不同的可选字段

下面的简单示例(我能想出的最好,简单的用例; 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();

我想要实现的是

  • 此构建器的用户必须提供一些最少的信息(因此系统可以正常工作),否则他将无法构建该对象
  • 在必填字段出现后,可以声明所有可选字段,没有特定顺序
  • 我可能需要为子项添加一些必填字段,但只需更改构建器中调用的第一个方法即可轻松处理
    • 我不想在这些课程之外进行演员表演(这里:在Main),但我不介意在这些代码中(这里:在动物或蜘蛛中)

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

我使用过的最有价值的资源

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;
//        }
    }


}

默认地将Impl

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);
    }
}

3 个答案:

答案 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(..)时返回您正在创建的动物的构建器。 此外,我不明白为什么有一个名称的构建器界面和另一个用于年龄的界面。
处理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();
        }

    }

我还没有测试过代码。

答案 2 :(得分:1)

您的代码问题是界面:

interface IAge {
    IBuild age(Integer age);
}

这将始终返回不带参数的基本IBuild接口,无论该实现是否使用某些参数来实现。实际上,即使使用参数返回它也不会用其他方法扩展构建器。

  1. 构建器中的参数需要是扩展的构建器,而不是要构建的类型。
  2. 所有用于通用参数的接口都必须进行参数设置,以允许适当的延续。

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