Google

Sep 19, 2013

When to use a builder design pattern? real life tips

Interview questions relating to design patterns are very popular in job interviews. Even if  this topic is not covered in rare occassions, you can bring it up yourself to open-ended questions to impress your  interviewers.

Q. What are the key difference(s) between a factory and a builder design patterns?
A. The builder design pattern builds an object over several steps. It holds the needed state for the target item at each intermediate step. The  StringBuilder is a good example that goes through to produce a final string. Here is a real world example that shows how builders are used instead of constructors to create Immutable objects. Creating immutable objects where applicable is a development best practice.


The factory design pattern describes an object that knows how to create several different but related kinds of object in one step, where the specific type is chosen based on given parameters. 
Q. When would you use a builder design pattern?
A
  • To construct a complex object. For example, to construct XML DOM objects and any other hierachichal objects. You have to create plenty of nodes and attributes to get your final object.
  •  Builder pattern makes your code more readable as explained in the article  "Using Builders instead of Constructors to create Immutable objects". The article explains how you can create immutable objects in Java by using the builder design pattern as opposed to using multiple constructors, which is known as the "telescoping constructor anti pattern".Firstly, let's see what is not so elegant about using a constructor as shown below with a CashBalance object that takes 3 BigDecimal arguments. Then we will see how a builder class will make your code more intuitive. 

Q. Can you give some examples from your experience?
A.

Example 1: Custom class.





package com.myapp.data;

import java.math.BigDecimal;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.util.Assert;


public class NetAsset
{
    
    public static NetAsset EMPTY = new Builder().setGrossAssetValueBeforeTax(BigDecimal.ZERO)
            .setGrossAssetValueAfterTax(BigDecimal.ZERO).setTotalAssets(BigDecimal.ZERO)
            .setTotalInvestments(BigDecimal.ZERO)
            .setTotalLiabilities(BigDecimal.ZERO)
            .setTotalReceivables(BigDecimal.ZERO)
            .build();
    
    private final BigDecimal grossAssetValueAfterTax;
    private final BigDecimal grossAssetValueBeforeTax;
    private final BigDecimal totalReceivables;
    private final BigDecimal totalInvestments;
    private final BigDecimal totalAssets;
    private final BigDecimal totalLiabilities;
    
    // add more
    
    private NetAsset(Builder builder)
    {
        Assert.notNull(builder.grossAssetValueAfterTax);
        Assert.notNull(builder.grossAssetValueBeforeTax);
        Assert.notNull(builder.totalReceivables);
        Assert.notNull(builder.totalInvestments);
        Assert.notNull(builder.totalAssets);
        Assert.notNull(builder.totalLiabilities);
        
        this.grossAssetValueAfterTax = builder.grossAssetValueAfterTax;
        this.grossAssetValueBeforeTax = builder.grossAssetValueBeforeTax;
        this.totalReceivables = builder.totalReceivables;
        this.totalInvestments = builder.totalInvestments;
        this.totalAssets = builder.totalAssets;
        this.totalLiabilities = builder.totalLiabilities;
    }
    
    public BigDecimal getGrossAssetValueAfterTax()
    {
        return grossAssetValueAfterTax;
    }
    
    public BigDecimal getGrossAssetValueBeforeTax()
    {
        return grossAssetValueBeforeTax;
    }
    
    public BigDecimal getTotalReceivables()
    {
        return totalReceivables;
    }
    
    public BigDecimal getTotalInvestments()
    {
        return totalInvestments;
    }
    
    public BigDecimal getTotalAssets()
    {
        return totalAssets;
    }
    
    public BigDecimal getTotalLiabilities()
    {
        return totalLiabilities;
    }
    
    @Override
    public String toString()
    {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
    
 
 //inner builder class
    public static class Builder
    {
        private BigDecimal grossAssetValueAfterTax;
        private BigDecimal grossAssetValueBeforeTax;
        private BigDecimal totalReceivables;
        private BigDecimal totalInvestments;
        private BigDecimal totalAssets;
        private BigDecimal totalLiabilities;
        
        // add more
        
        public Builder setGrossAssetValueAfterTax(BigDecimal grossAssetValueAfterTax)
        {
            this.grossAssetValueAfterTax = grossAssetValueAfterTax;
            return this;
        }
        
        public Builder setGrossAssetValueBeforeTax(BigDecimal grossAssetValueBeforeTax)
        {
            this.grossAssetValueBeforeTax = grossAssetValueBeforeTax;
            return this;
        }
        
        public Builder setTotalReceivables(BigDecimal totalReceivables)
        {
            this.totalReceivables = totalReceivables;
            return this;
        }
        
        public Builder setTotalInvestments(BigDecimal totalInvestments)
        {
            this.totalInvestments = totalInvestments;
            return this;
        }
        
        public Builder setTotalAssets(BigDecimal totalAssets)
        {
            this.totalAssets = totalAssets;
            return this;
        }
        
        public Builder setTotalLiabilities(BigDecimal totalLiabilities)
        {
            this.totalLiabilities = totalLiabilities;
            return this;
        }
        
        public NetAsset build()
        {
            return new NetAsset(this);
        }
    }
    
}


To use this class

  NetAsset.Builder builder = new NetAsset.Builder();
  builder.setGrossAssetValueBeforeTax(BigDecimal.valueOf("3500.00"))
         .setGrossAssetValueAfterTax(BigDecimal.valueOf("500.00"))
   .setTotalAssets(BigDecimal.valueOf("3500.00"))
         .setTotalReceivables(BigDecimal.valueOf("2500.00"));     


or

return NetAsset.EMPTY;


Example 2: The Mock objects library classes.

m.expects(once())
    .method("method1")
    .with(eq(1), eq(2))
    .returns("someResponse");


Example 3: The StringBuilder class.

  return new StringBuilder("initial text")
            .append("more text")
   .append("some more text").toString();



Example 4: Apache Camel route builders.

   private void configureJobSweeperRoute()
   {
         from(TIMER_START_UP)
              .routeId("JobRecovery")
              .delay(RECOVERY_JOB_KICKIN_DELAY)
              .log(INFO, "Start checking incompleted jobs on start up")
              .bean(recoveryService, "startUpJobRecovery")
              .end();

    }


So, it is very widely used.

Other design patterns - real life examples

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home