vendredi 16 octobre 2020

Fluent builder with inherited abstract class

I'm trying to make a small hierarchy of classes to use the Builder pattern. There is only one abstract class (sort of like a base class with common fields). This one has close to 15 fields/members, hence the builder approach.

This is how the abstract class looks like:

public abstract class AbstractItem {
  private Long id;

  private String type;

  private String status;

  protected AbstractItem () { }

  protected AbstractItem (final BuilderBase<?> builder) {
    id = builder.id;
    type = builder.type;
    status = builder.status;
  }

  public Long getId() { return id; }

  public String getType() { return type; }

  public String getStatus() { return status; }

  protected abstract static class BuilderBase<T extends BuilderBase<T>> {
    private Long id;

    private String type;

    private String status;

    protected abstract T self();

    public T withId(final Long value) {
      id = value;
      return self();
    }

    public T withType(final String value) {
      type = value;
      return self();
    }

    public T withStatus(final String value) {
      status = value;
      return self();
    }

    // PROBLEM 1
    // public abstract T build(); // Nice to have!
  }
}

...and this could be one of the classes that extend from it:

public final class Item extends AbstractItem {
  private String device;

  public Item() { }

  public Item(final BuilderBase<?> builder) {
    super(builder);
    device = builder.device;
  }

  public static BuilderBase<?> builder() { return new Builder(); }

  public String getDevice() { return device; }

  public static class BuilderBase<T extends BuilderBase<T>> extends AbstractItem.BuilderBase<T> {
    private String device;

    // PROBLEM 2
    @Override
    protected T self() { throw new IllegalStateException("Can't touch this!"); }

    public T withDevice(final String value) {
      device = value;
      return self();
    }

    public Item build() { return new Item(this); }
  }

  protected static class Builder extends BuilderBase<Builder> {
    @Override
    protected Builder self() { return this; }
  }
}

As it stands right now, it works:

return Item.builder()
    .withId(1_100L)
    .withType("ITEM_01")
    .withStatus("CREATED")
    .withDevice("MAGIC_SWORD")
    .build();

But I have two problems here:

  • Is there a way to implement correctly protected T self() for inherited classes? — so far I can only return null from those or throw an exception.
  • Is there a way to declare public abstract T build() (in the abstract BuilderBase class) in such a way that classes that inherit from it must have to provide their own implementation?

Aucun commentaire:

Enregistrer un commentaire