0

I have two types of payload coming from upstream: It's either PayloadA or PayloadB. PayloadA has lot of fields in it as compared to PayloadB but there are some common fields between PayloadA and PayloadB. Just to make example simpler, I have added only few fields.

Below is the builder class for 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
}

Now below is the class for 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
}

Now I have created another class which is Payload class in which I have all the common fields both for PayloadA and PayloadB so I have to set these fields as well somehow and I am not sure how to use below class:

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

  // getters and setters here
}

Question:

Now from the below code I am making either PayloadA or PayloadB depending on what is passed.

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

And in the DataProcessor process method:

  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?
  }

Now how can I set createTimestamp and key variable which is in Payload class in the process method? Right now I have a run method where I am differentiating it but in general I will have a different upstream code for PayloadA and different upstream code for PayloadB so depending on that we will use either of the Payload class.

Also should I have two different builders here or one big builder here doing everything?

john
  • 11,311
  • 40
  • 131
  • 251

3 Answers3

1

PayloadA and PayloadB can extend Payload as shown below:

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

How can I figure out whether payload is PayloadA or PayloadB inside process()?

You can find that using instanceof like payload instanceof PayloadA as shown above. But, in general, it is not a good idea to code using of instanceof checks, so don't prefer to use it unless it can't be avoided.

Should I have two different builders here or one big builder here doing everything?

As per your provided code above, the fields are quite PayloadA and PayloadB different, so better to keep separate beans and respective builders.

UPDATE: I need to figure out what type of payload it is and basis on that I need to set values for key variable?

Inside setKey() will be called on the object type passed to the process(Payload payload) (polymorphism, one of the fundamental principles of OOP) i.e., if you pass PayloadA object from run() method, setKey() on PayloadA object will be called. To summarise, you don't need instanceof checks at all for this. It is upto your requirement where do you want to set the Key, it could be inside process() method (if you have some other dependencies to generate the key) or it could be done as suggested by @Roberto

Vasu
  • 21,832
  • 11
  • 51
  • 67
  • So if I don't use instanceOf then what should I be using? Bcoz in the process method, I need to figure out what type of payload it is and basis on that I need to set values for `key` variable? – john Dec 06 '16 at 22:29
  • instead of instanceof you could add an abstract method in Payload, and override it in PayloadA and PayloadB. you could then call such method from process, and the proper implementation would be used. – Roberto Attias Dec 06 '16 at 22:31
  • @RobertoAttias Can you provide an example for that so that I can understand better. I am slightly confuse on that of how it will work. – john Dec 06 '16 at 22:32
  • @david As I mentioned in my code above, according to the object passed, `key` will be set, so you don't need to use `instanceof` checks at all. – Vasu Dec 06 '16 at 22:32
  • @javaguy Perhaps it would be help to show an example `Payload` class? – Ken Wayne VanderLinde Dec 06 '16 at 22:51
1
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();
}

Edit:

The idea here is that any class extending payload must either implement the setKey() method, or be abstract itself. so, PayloadA and PayloadB both implement the method. Each class provides a different implementation.

Now suppose you do

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

As expected, the implementation actually executed would be the one defined in PayloadA.

Now consider this case:

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

Despite the variable being declared of type Payload, the actual type of the object referred to by the variable is PayloadA, hence the setKey() call is the one in PayloadA. This is referred to as Dynamic Dispatching, because which implementation is called is known at run-time, not at compile time

Roberto Attias
  • 1,883
  • 1
  • 11
  • 21
  • Although your code seems very straightforward for anyone familiar with OOP, the OP does not seem to be strong in the concept of polymorphism. You should add supporting text explaining what you're doing and why you're doing it. – Ken Wayne VanderLinde Dec 06 '16 at 22:49
0
public class Payload {
 // common fields
 private String clientid;
 private String key;
 private long Timestamp;

 // setter and getters 
}

public class PayloadA extends Payload implements PayloadStrategy {
 // PayloadA specific fields
 private String langid;
 private String deviceId;

 // setters and getters

 public static class Builder {
 // existing code
 } 

 @Override
 public void process() {
    System.out.println("PayloadA specific implementation");
 }

}

public class PayloadB extends Payload implements PayloadStrategy {
 // PayloadA specific fields
 private String type;
 private String payId;

 // setters and getters

 public static class Builder {
 // existing code
 }

 @Override
 public void process() {
    System.out.println("PayloadB specific implementation");
 }
}

}

// define contract
public interface PayloadStrategy {
 public void process();
}

// payload context
public class PayloadContext {

 PayloadStrategy strategy;

 public void setContext(PayloadStrategy payloadStrategy) {
    this.strategy = payloadStrategy;
 }

 public void processPayload() {
    strategy.process();
 }
}

// parameterized (PayloadA or PayloadB) payload run method 

PayloadContext context = new PayloadContext();
context.setContext(new PayloadA());
context.processPayload();
fabfas
  • 2,200
  • 1
  • 21
  • 21