Builder Design Pattern with Java

The Problem: Not using the right pattern for the right situation and creating difficult-to-find bugs.

In day-to-day work I continually see complex objects being constructed without any patterns at all. What happens? Well, we see giant Services with 10, 20, 30, or more lines with setters from the objects being populated. Do you think we can continue coding like this? Of course not!

This structure looks like procedural programming. We must learn the best programming techniques so that we can deliver quality software and avoid bugs. If we use the Builder pattern effectively, we can make awesome and flexible code.

Builder

The diagram:

Builder.PNG

Get the source code:
https://github.com/rafadelnero/design-patterns-saga

1 – Calculation Class: This is a simple class with values of a calculation. It has attributes, getters and setters, and some important details in this class:

  • The setters are private to avoid direct access
  • The getter of the list is unmodifiable to avoid new elements adding
  • The constructor is private for avoiding instantiation

We are using a really important concept here, ENCAPSULATION! But, how can I change those fields if they are private? You will see it in the next topic.

public class Calculation {

  private long idCalculation;

  private long idPeriodicity;

  private List<Price> prices;

  private Date dtCalculation;

  private Calculation() {
    prices = new ArrayList<>();
  }

  // Builder class omitted, it will be explained in the next topic

  private final void setIdCalculation(long idCalculation) {
    this.idCalculation = idCalculation;
  }

  private final void setIdPeriodicity(long idPeriodicity) {
    this.idPeriodicity = idPeriodicity;
  }

  private final void setDtCalculation(Date dtCalculation) {
    this.dtCalculation = dtCalculation;
  }

  public final long getIdCalculation() {
    return idCalculation;
  }

  public final long getIdPeriodicity() {
    return idPeriodicity;
  }

  public final Date getDtCalculation() {
    return dtCalculation;
  }

  public final Collection<Price> getPrices() {
    return Collections.unmodifiableCollection(prices);
  }
}

2 – Static Inner Builder Class: Here is where the magic happens! We are going to build the Calculation object and with the Builder we can have access to modify the fields.

In the constructor:

  • public Builder(long idCalculation, long idPeriodicity): we are initiating the Builder class with the required fields, that is why they are being passed in the constructor.
  • In the method Builder withDate(Date dtCalculation): we are putting information in dtCalculation. Most importantly we are returning “this”, the Builder. It makes  invoking another method directly possible.
  • The method public Builder withPrice(long idCurrency, BigDecimal priceValue) builds the Price object. See that this method delegates the responsibility for the Builder of the Price class.
public static class Builder {

  private Calculation calculation = new Calculation();

  public Builder(long idCalculation, long idPeriodicity) {
    calculation.setIdCalculation(idCalculation);
    calculation.setIdPeriodicity(idPeriodicity);
  }

  public Builder withDate(Date dtCalculation) {
    calculation.setDtCalculation(dtCalculation);

    return this;
  }

  public Builder withPrice(long idCurrency, BigDecimal priceValue) {
    Price price = new Price.Builder(new Random().nextLong()).withPrice(idCurrency, priceValue).build();
    calculation.prices.add(price);

    return this;
  }

  public Calculation build() {
    return calculation;
  }
}

3 – Price Class: This is pretty much the same as the Calculation class – no new concept here!

public class Price {

  private long idPrice;
  private long idCurrency;
  private BigDecimal priceValue;

  private Price() {
    super();
  }

  public static class Builder {

    private Price price = new Price();

    public Builder(long idPrice) {
      price.setIdPrice(idPrice);
    }

    public Builder withPrice(long idCurrency, BigDecimal value) {
      price.setIdCurrency(idCurrency);
      price.setPriceValue(value);

      return this;
    }

    public Price build() {
      return this.price;
    }

  }

  // Getters and Setters omitted

}

4 – Unitary Test: The final process. Let’s finally build our objects!

Summary of actions:

1 – We encapsulate the attributes of the POJO
2 – We make the constructor of the POJO private
3 – We create a static Builder class inside the POJO class
4 – We declare the POJO class inside the Builder
5 – We create methods in the Builder that return the Builder
6 – Finally, we build the object

You can execute your tests with these Unitary Tests:

public class BuilderTest {

  private static final long ID_CALCULATION = 1L;

  private static final long ID_PERIODICITY = 1L;

  private static final long ID_CURRENCY = 1L;

  private static final Date DT_CALCULATION = new Date();

  private static final BigDecimal PRICE_VALUE_1 = BigDecimal.ONE;
  private static final BigDecimal PRICE_VALUE_2 = BigDecimal.TEN;

  @Test
  public void validateCalculationInformationTest() {
    Calculation calculation = new Calculation.Builder(ID_CALCULATION, ID_PERIODICITY)
        .withDate(DT_CALCULATION)
        .withPrice(ID_CURRENCY, PRICE_VALUE_1)
        .withPrice(ID_CURRENCY, PRICE_VALUE_2).build();

    Assert.assertEquals(ID_CALCULATION, calculation.getIdCalculation());
    Assert.assertEquals(ID_PERIODICITY, calculation.getIdPeriodicity());
    Assert.assertEquals(DT_CALCULATION, calculation.getDtCalculation());

    Iterator<Price> iterator = calculation.getPrices().iterator();
    Price firstPrice = iterator.next();
    Price secondPrice = iterator.next();

    Assert.assertEquals(ID_CURRENCY, firstPrice.getIdCurrency());
    Assert.assertEquals(PRICE_VALUE_1, firstPrice.getPriceValue());

    Assert.assertEquals(ID_CURRENCY, secondPrice.getIdCurrency());
    Assert.assertEquals(PRICE_VALUE_2, secondPrice.getPriceValue());
  }

  @Test(expected = UnsupportedOperationException.class)
  public void validatePricesIsImmutableTest() {
    Calculation calculation = new Calculation.Builder(ID_CALCULATION, ID_PERIODICITY).build();

    calculation.getPrices().add(new Price.Builder(new Random()
               .nextLong()).withPrice(ID_CURRENCY, PRICE_VALUE_1).build());
  }

}

To practice this Pattern, create another POJO and check if the fields correspond to each other in your Unitary Test.

Keep up the great work. Stay tuned for more articles next week!

Written by
Rafael del Nero
Join the discussion

4 comments