Thursday, 6 April 2017

Builder Design Pattern in Java - Its uses in automation

          The builder pattern is an alternative way to construct complex objects. This should be used only when you want to build different immutable objects using same object building process. Builder pattern aims to Separate the construction of a complex object from its representation so that the same construction process can create different representations.

Why do we need Builder Pattern:
      We all know that constructors in java are used to create objects and takes the parameters that are required create them. The problem starts when we have lot of parameters and some of them may be mandatory and some are optional. 

    To illustrate the problem, lets take an example of ordering ice cream online. There are multiple varieties of the ice creams available currently in the market.Each person has different choices of toppings, flavors etc. Now consider that you are creating an object for each user's choice. In this case you will end up creating multiple constructors based each user's choice. Also, if you pass the value of one parameter to other parameter, the whole thing would mess up.

Problems:
1. Too many constructors
2. Error prone because of multiple parameters
3. The order has to be followed while creating the object.

Builder pattern solves all the above problems. Lets see an implementation example.

============================================================
public class BuilderPatternValidator {
  public static void main(String args[]) {
    // Creating object using Builder pattern in java
    IceCream MilkIceCream = new       IceCream.Builder().milk(1).chocochips(2).caramel(2).dryfuits(1.5).m_ms(0.75).build();
    IceCream SoyaIceCream = new IceCream.Builder().soya(1).peanuts(2).caramel(2).dryfuits(1.5).m_ms(0.75).build();

    // IceCream is ready to eat :)
    System.out.println(MilkIceCream);
    System.out.println(SoyaIceCream);
  }
}

class IceCream {
  private final double milk; // cup
  private final double soya; // cup
  private final int chocochips; // spoon
  private final int caramel; // spoon
  private final double peanuts; // cup
  private final double dryfuits; // spoon
  private final double m_ms; // cup
  private final int cherry; // number

  public static class Builder {
    private double milk; // cup
    private double soya; // cup
    private int chocochips; // spoon
    private int caramel; // spoon
    private double peanuts; // spoon
    private double dryfuits; // spoon
    private double m_ms; // cup
    private int cherry; // number

    // builder methods for setting property
    public Builder milk(double cup) {
      this.milk = cup;
      return this;
    }

    public Builder soya(double cup) {
      this.soya = cup;
      return this;
    }

    public Builder chocochips(int spoon) {
      this.chocochips = spoon;
      return this;
    }

    public Builder caramel(int spoon) {
      this.caramel = spoon;
      return this;
    }

    public Builder peanuts(int spoon) {
      this.peanuts = spoon;
      return this;
    }

    public Builder dryfuits(double spoon) {
      this.dryfuits = spoon;
      return this;
    }

    public Builder m_ms(double spoon) {
      this.m_ms = spoon;
      return this;
    }

    public Builder cherry(int number) {
      this.cherry = number;
      return this;
    }

    // return fully build object
    public IceCream build() {
      return new IceCream(this);
    }
  }

  // private constructor to enforce object creation through builder
  private IceCream(Builder builder) {
    this.milk = builder.milk;
    this.soya = builder.soya;
    this.chocochips = builder.chocochips;
    this.caramel = builder.caramel;
    this.peanuts = builder.peanuts;
    this.dryfuits = builder.dryfuits;
    this.m_ms = builder.m_ms;
    this.cherry = builder.cherry;
  }

  @Override
  public String toString() {
    return "IceCream { " + "milk=" + milk + ", soya=" + soya + ", chocochips=" + chocochips + ", caramel="
        + caramel + ", peanuts=" + peanuts + ", dryfuits=" + dryfuits + ", m_ms=" + m_ms
        + ", cherry=" + cherry + '}';

  }
}
=========================================================
Points to be noted from the above example are: 

  1. IceCream constructor is private, which means that this class can not be directly    instantiated from the client code. 
  2. While creating the objects, I have declared only the parameters that are needed
  3. The order of the parameters can be varied.
  4. Code looks more readable. 

Advantages:
1) more maintainable if number of fields required to create object is more than 4 or 5.
2) less error-prone as user will know what they are passing because of explicit method call.
3) more robust as only fully constructed object will be available to client.

Disadvantages:
1) verbose and code duplication as Builder needs to copy all fields from Original or IceCream class.

How to use this builder pattern in Automation:
      The builder pattern is very handy particularly in API automation, where we deal with large number of parameters. While creating the requests, we use pojo classes. This builder pattern can be used to construct the object with the required parameters and pass it to pojo classes. This way we can reduce lot of code duplication.

No comments:

Post a Comment