Подкласс абстрактного класса в шаблоне строителя?

У меня есть два типа полезных данных, поступающих из восходящего потока: это либо PayloadA, либо PayloadB. PayloadA имеет много полей по сравнению с PayloadB, но есть некоторые общие поля между PayloadA и PayloadB. Чтобы сделать пример проще, я добавил только несколько полей.

Ниже приведен класс построителя для PayloadA:

public final class PayloadA {
  private final String clientId;
  private final String langid;
  private final String deviceId;
  private final Map<String, String> applicationPayload;
  // other fields as well

  private PayloadA(Builder builder) {
    this.clientId = builder.clientId;
    this.langid = builder.langid;
    this.deviceId = builder.deviceId;
    this.applicationPayload = builder.applicationPayload.build();
  }

  public static class Builder {
    protected final String deviceId;
    protected String clientId;
    protected String langid;
    protected ImmutableMap.Builder<String, String> applicationPayload = ImmutableMap.builder();

    public Builder(String deviceId) {
      this.deviceId = deviceId;
    }

    public Builder setClientId(String clientId) {
      this.clientId = clientId;
      return this;
    }

    public Builder setLangid(String langid) {
      this.langid = langid;
      return this;
    }

    public Builder setPayload(Map<String, String> payload) {
      this.applicationPayload.putAll(payload);
      return this;
    }

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

    // getters and to string here
}

Ниже приведен класс для PayloadB:

public final class PayloadB {
  private final String clientid;
  private final String type;
  private final String payId;
  // other fields as well

  private PayloadB(Builder builder) {
    this.clientid = builder.clientid;
    this.type = builder.type;
    this.payId = builder.payId;
  }

  public static class Builder {
    protected final String type;
    protected String payId;
    protected String clientid;

    public Builder(String type) {
      this.type = type;
    }

    public Builder setPayId(String payId) {
      this.payId = payId;
      return this;
    }

    public Builder setClientId(String clientid) {
      this.clientid = clientid;
      return this;
    }

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

    // getters and to string here
}

Теперь я создал еще один класс, который является классом Payload, в котором у меня есть все общие поля как для PayloadA, так и для PayloadB, поэтому мне также нужно как-то установить эти поля, и я не уверен, как использовать класс ниже:

public abstract class Payload {
  private long createTimestamp;
  private String key;
  // some other fields are here

  // getters and setters here
}

Вопрос:

Теперь из приведенного ниже кода я делаю либо PayloadA, либо PayloadB в зависимости от того, что передается.

  private void run(String name) {
    // .. some code here
    if (name.equalsIgnoreCase("PayloadA")) {
      Payload payload =
          new PayloadA.Builder(getDeviceId()).setClientId("someid").setLangid("anotherid")
              .setPayload("some map").build();
      DataProcessor.getInstance().process(payload);
    } else {
      Payload payload =
          new PayloadB.Builder(getType()).setPayId("someid").setClientId("anotherid").build();
      DataProcessor.getInstance().process(payload);
    }
  }

И в методе DataProcessor process:

  private void process(Payload payload) {
    // 1) here I need to set createTimestamp and key variables on payload bcoz they are common
    // fields.
    // 2) Also how can I figure out whether payload is PayloadA or PayloadB here?
  }

Теперь, как я могу установить переменные createTimestamp и key, которые находятся в классе Payload, в методе process? Прямо сейчас у меня есть метод запуска, в котором я различаю его, но в целом у меня будет другой исходный код для PayloadA и другой восходящий код для PayloadB, поэтому в зависимости от этого мы будем использовать любой из классов Payload.

Также я должен иметь здесь двух разных строителей или одного большого строителя, который будет делать все?


person john    schedule 06.12.2016    source источник


Ответы (2)


PayloadA и PayloadB могут расширять Payload, как показано ниже:

public abstract class Payload {

     private long createTimestamp;
     private String key;
     // some other fields are here
     // getters and setters here
}

public class PayloadA extends Payload  {
    //add existing code
}
public class PayloadB extends Payload {
    //add existing code
}

private void process(Payload payload) {

     //Depending upon the object passed, fields will be set for A or B       

     payload.setCreateTimestamp(ADD_DATA1);
     payload.setKey(ADD_DATA2);
     //set other fields

    //if(payload instanceof PayloadA) {
        //payloadA
    //}
}

Как я могу выяснить, является ли полезная нагрузка PayloadA или PayloadB внутри process()?

Вы можете найти это, используя instanceof как payload instanceof PayloadA как показано выше. Но, в общем, не рекомендуется кодировать использование проверок instanceof, поэтому не используйте их, если только этого нельзя избежать.

Должен ли я иметь здесь двух разных строителей или одного большого строителя, который будет делать все?

Согласно приведенному выше коду, поля довольно PayloadA и PayloadB отличаются, поэтому лучше хранить отдельные bean-компоненты и соответствующие компоновщики.

ОБНОВЛЕНИЕ: мне нужно выяснить, какой это тип полезной нагрузки, и на основании этого мне нужно установить значения для ключевой переменной?

Внутри setKey() будет вызываться тип объекта, переданный в process(Payload payload) (полиморфизм, один из фундаментальных принципов ООП), т. е. если вы передаете PayloadA объект из метода run(), будет вызываться setKey() объект PayloadA. Подводя итог, вам вообще не нужны проверки instanceof для этого. Это зависит от вашего требования, где вы хотите установить ключ, он может быть внутри метода process() (если у вас есть какие-то другие зависимости от сгенерируйте key) или это можно сделать, как предложил @Roberto

person developer    schedule 06.12.2016
comment
Итак, если я не использую instanceOf, что мне следует использовать? Bcoz в методе процесса мне нужно выяснить, какой это тип полезной нагрузки, и на основании этого мне нужно установить значения для переменной key? - person john; 07.12.2016
comment
вместо instanceof вы можете добавить абстрактный метод в Payload и переопределить его в PayloadA и PayloadB. затем вы можете вызвать такой метод из процесса, и будет использоваться правильная реализация. - person Roberto Attias; 07.12.2016
comment
@RobertoAttias Можете ли вы привести пример, чтобы я мог лучше понять. Я немного запутался в том, как это будет работать. - person john; 07.12.2016
comment
@david Как я уже упоминал в своем коде выше, в соответствии с переданным объектом будет установлено key, поэтому вам вообще не нужно использовать проверки instanceof. - person developer; 07.12.2016
comment
@javaguy Возможно, было бы полезно показать пример класса Payload? - person Ken Wayne VanderLinde; 07.12.2016

abstract public class Payload {
  abstract void setKey();
}

public class PayloadA extends Payload  {
    //add existing code
   void setKey() {
      key = "a";
   }
}
public class PayloadB extends Payload {
    //add existing code
  void setKey() {
    key = "b";
  }
}

private void process(Payload payload) {

     //Depending upon the object passed, fields will be set for A or B       

     payload.setCreateTimestamp(ADD_DATA1);
     payload.setKey();
}

Изменить:

Идея здесь в том, что любой класс, расширяющий полезную нагрузку, должен либо реализовывать метод setKey(), либо сам быть абстрактным. таким образом, PayloadA и PayloadB реализуют этот метод. Каждый класс обеспечивает различную реализацию.

Теперь предположим, что вы делаете

PayloadA pa = new PayloadA();
pa.setKey()

Как и ожидалось, фактически выполненная реализация будет той, которая определена в PayloadA.

Теперь рассмотрим этот случай:

Payload pa = new PayloadA();
pa.setKey()

Несмотря на то, что объявлена ​​переменная типа Payload, фактическим типом объекта, на который ссылается переменная, является PayloadA, поэтому вызов setKey() — это тот же, что и в PayloadA. Это называется динамической диспетчеризацией, потому что вызываемая реализация известна во время выполнения, а не во время компиляции.

person Roberto Attias    schedule 06.12.2016
comment
Хотя ваш код кажется очень простым для тех, кто знаком с ООП, ОП не кажется сильным в концепции полиморфизма. Вы должны добавить вспомогательный текст, объясняющий, что вы делаете и почему вы это делаете. - person Ken Wayne VanderLinde; 07.12.2016