Sunday, May 19, 2013

Desing Patterns in Java - builder pattern

Static factories and constructors share a limitation: they do not scale well to large
numbers of optional parameters.
Consider the case of computer configuration builder. There are parts that are essential in this build, but also there are optional parts.

There number of ways that developers usually try to approach this problem. First is  telescopic constructors where you need to create constructor for each of optional parameter. This approach has obvious disadvantages (ugly code, lot of work). Second approach is to use JavaBeans in which you call a parameterless constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interest. There are two problems with this approach. First is that JavaBean may be in an inconsistent state partway through its construction. Second is that this pattern pre-cludes the possibility of making a class immutable.
There is however third approach that is very elegant and can create immutable objects. It is a form of the Builder pattern.Instead of making the desired object directly, the client calls a constructor (or static factory) with all of the required parameters and gets a builder object. Here is how it looks like:
public class ComputerConfigurator {    
    private final int sizeOfRam;
    private final int sizeOfHdd;
    private final int processorSpeed;    
    private final int dedicatedGpuSpeed;
    private final boolean waterCooling;
    
    public static class Builder {
        //Required params
        private final int sizeOfRam;
        private final int sizeOfHdd;
        private final int procesorSpeed;        
        //Optional params
        private int dedicatedGpuSpeed;
        private boolean waterCooling;

        public Builder(int sizeOfRam, int sizeOfHdd, int procesorSpeed) {
            this.sizeOfRam = sizeOfRam;
            this.sizeOfHdd = sizeOfHdd;
            this.procesorSpeed = procesorSpeed;
        }
        
        public Builder dedicatedGpuSpeed(int val) {
            dedicatedGpuSpeed = val;
            return this;
        }
        
        public Builder waterCooling(boolean val) {
            waterCooling = val;
            return this;
        }
        
        public ComputerConfigurator build() {
            return new ComputerConfigurator(this);
        }
        
    }
    
    private ComputerConfigurator(Builder builder) {
        sizeOfRam = builder.sizeOfRam;
        sizeOfHdd = builder.sizeOfHdd;
        processorSpeed = builder.procesorSpeed;
        dedicatedGpuSpeed = builder.dedicatedGpuSpeed;
        waterCooling = builder.waterCooling;
    }
}
The builder’s setter methods return the builder itself so that invocations can be chained. Here’s how the client code looks:
public class Main {
    
  public static void main(String[] args) {
    ComputerConfigurator firstComputerConfigurator = new ComputerConfigurator.
            Builder(2, 40, 3000).
            dedicatedGpuSpeed(1000).build();
        
    ComputerConfigurator secondComputerConfigurator = new ComputerConfigurator.
            Builder(2, 40, 3000).
            dedicatedGpuSpeed(1000).waterCooling(true).build();
  }    
}

No comments:

Post a Comment