如何子类化Java构建器类?

时间:2016-12-20 07:54:46

标签: java design-patterns abstract-class builder builder-pattern

我有两个构建器 - PayloadAPayloadB。为了使示例更简单,我删除了许多其他字段。

  • PayloadA.Builder构造函数将processNamegenericRecord作为输入参数,然后从genericRecord中提取一些内容。在那我正在做验证。
  • PayloadB.Builder构造函数也将processNamegenericRecord作为输入参数,然后与上面的genericRecord相比,它提取了一些不同的东西。在这些不同的领域,我正在进行验证。

正如您所看到的,这两个Payload?.Builder之间的共同点是processNamegenericRecord,提取oldTimestamp值,然后是isValid方法。

以下是我的PayloadA课程:

public final class PayloadA {
  private final String clientId;
  private final String deviceId;
  private final String processName;
  private final GenericRecord genericRecord;
  private final Long oldTimestamp;

  private PayloadA(Builder builder) {
    this.clientId = builder.clientId;
    this.deviceId = builder.deviceId;
    this.processName = builder.processName;
    this.genericRecord = builder.genericRecord;
    this.oldTimestamp = builder.oldTimestamp;
  }

  public static class Builder {
    private final String processName;
    private final GenericRecord genericRecord;
    private final String clientId;
    private final String deviceId;
    private final Long oldTimestamp;

    public Builder(PayloadA payload) {
      this.processName = payload.processName;
      this.genericRecord = payload.genericRecord;
      this.clientId = payload.clientId;
      this.deviceId = payload.deviceId;
      this.oldTimestamp = payload.oldTimestamp;
    }

    public Builder(String processName, GenericRecord genericRecord) {
      this.processName = processName;
      this.genericRecord = genericRecord;
      this.clientId = (String) DataUtils.parse(genericRecord, "clientId");
      this.deviceId = (String) DataUtils.parse(genericRecord, "deviceId");
      this.oldTimestamp = (Long) DataUtils.parse(genericRecord, "oldTimestamp");
    }

    // calling this method to validate
    public boolean isValid() {
      return isValidClientIdDeviceId();
    }

    private boolean isValidClientIdDeviceId() {
        // validate here
    }

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

  // getter here
}

以下是我的PayloadB课程:

public final class PayloadB {
  private final GenericRecord genericRecord;
  private final String processName;
  private final String type;
  private final String datumId;
  private final Long oldTimestamp;

  private PayloadB(Builder builder) {
    this.processName = builder.processName;
    this.genericRecord = builder.genericRecord;
    this.type = builder.type;
    this.datumId = builder.datumId;
    this.oldTimestamp = builder.oldTimestamp;
  }

  public static class Builder {
    private final GenericRecord genericRecord;
    private final String processName;
    private final String type;
    private final String datumId;
    private final Long oldTimestamp;

    public Builder(PayloadB payload) {
      this.processName = payload.processName;
      this.genericRecord = payload.genericRecord;
      this.type = payload.type;
      this.datumId = payload.datumId;
      this.oldTimestamp = payload.oldTimestamp;
    }

    public Builder(String processName, GenericRecord genericRecord) {
      this.processName = processName;
      this.genericRecord = genericRecord;
      this.type = (String) DataUtils.parse(genericRecord, "type");
      this.datumId = (String) DataUtils.parse(genericRecord, "datumId");
      this.oldTimestamp = (Long) DataUtils.parse(genericRecord, "oldTimestamp");
    }

    // calling this method to validate
    public boolean isValid() {
      return isValidType() && isValidDatumId();
    }

    private boolean isValidType() {
        // validate here
    }

    private boolean isValidDatumId() {
        // validate here
    }

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

    // getter here

}

现在有什么办法可以在这里使用抽象类的概念吗?我可以创建一个抽象类Payload但是我的抽象类应该是什么东西:

public final class PayloadA extends Payload { ... }
public final class PayloadB extends Payload { ... }

然后,一旦我构建了我的构建器,我将把它传递给其他方法,并且我想使用getter访问所有字段。所以,让我们说我已经构建了PayloadA,所以我将发送执行方法,如下所示,然后在该方法中,我想提取PayloadA的所有字段。同样,如果我发送PayloadB执行方法,那么我想使用getter提取PayloadB类的所有字段。我怎么能这样做?

private void execute(Payload payload) {

    // How can I access fields of PayloadA or PayloadB 
    // depending on what was passe
}

2 个答案:

答案 0 :(得分:1)

仅当所提到的字段不巧合时才为有效负载创建超类。您可以在那里移动常用字段和方法(但不能移动构建器)。你甚至可以为构建器创建一个超类,但它可能会使代码混乱太多。

如果您确实可以使用有效负载超级类,那么您可以使用Visitor Pattern实现execute方法:

首先,您必须创建一个访问您可以访问具体类的访问者:

public class PayloadVisitor {

    public void visit(PayloadA payloadA) {
        // use payload A here
    }

    public void visit(PayloadB payloadB) {
        // use payload B here
    }
}

然后你必须向你的超级类添加一个接受访问者的方法:

public abstract class Payload {

    // common fields and methods

    public abstract void accept(PayloadVisitor visitor);
}

覆盖子类中的方法accept

public final class PayloadA extends Payload {

    // ...

    @Override
    public void accept(PayloadVisitor visitor) {
        visitor.visit(this);
    }
}

public final class PayloadB extends Payload {

    // ...

    @Override
    public void accept(PayloadVisitor visitor) {
        visitor.visit(this);
    }
}

您的方法execute只是将调用重定向到相应的visit方法:

private void execute(Payload payload) {
    payload.accept(new PayloadVisitor());
}

访客模式可能令人难以招架。您也可以保持简单,并使用instanceof来确定具体的类。

答案 1 :(得分:0)

我认为这里的问题是PayloadA和PayloadB是否正在为设计分享一些完全相同的东西。如果逻辑在某种程度上与一个参数相同,则可以有一个类。

也许您可以拥有抽象类,并且对于特定字段的实现,您可以返回特定实现的具体值。

例如,抽象类具有字段的抽象setter / getter,当您将该方法实现到PayloadA和PayloadB时,您可以返回所需的字段。

我认为问题在于设计在这里不是如何做到的。看看你的课程到底是什么,然后你有很多选择