Optional Parameters Handling Strategy In Java And Comparision

Optional Parameters handling Strategy

Often we face a situation where we need to design an Object which expects lot of parameters from client. Some of parameters are required and some of them are optional. In this article we will focus on various strategies by which we can design such objects and their pros and cons

Strategy 1. Telescopic constructors
To design such objects, we can use chain of overloading constructors. First minimal constructor is taking only required parameters then delegate call another constructor which takes an optional parameter
Then this constructor calls another which takes another optional parameter until all optional parameters are covered.  When calling another constructor, it can pass default value for optional parameter.

Pros:
1.    For less number of parameters this is good way to design object.
2.    Very easy to implement.
Cons:
1.    For large number of parameters, it creates problems. Developer often confuse while passing parameters.
2.    If two adjacent parameters have same datatype and developer unintentionally swap values. Compiler not complain but it creates a genuine problem in runtime and very hard to track.

Example:
package com.example.builder;

public class EmployeeTelescopic {
      
       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress ;
      
      
       public EmployeeTelescopic(String name,Integer empId,String company)
       {
              this(name,empId,company,0);
       }
      
       public EmployeeTelescopic(String name,Integer empId,String company,int passport)
       {
              this(name,empId,company,passport,"NA");
       }
      
       public EmployeeTelescopic(String name,Integer empId,String company,Integer passport,String tempAddress)
       {
              this.name=name;
              this.empId=empId;
              this.company=company;
              this.passport=passport;
              this.tempAddress=tempAddress;
             
       }
      
      
      
      
       @Override
       public String toString() {
              return "EmployeeTelescopic [name=" + name + ", empId=" + empId
                           + ", company=" + company + ", passport=" + passport
                           + ", tempAddress=" + tempAddress + "]";
       }

       public static void main(String[] args) {
             
              EmployeeTelescopic emp = new EmployeeTelescopic("Shamik",100,"IBM");
              EmployeeTelescopic emp1 = new EmployeeTelescopic("Akash",101,"IBM",1234,"1,bangalore");
              System.out.println(emp);
              System.out.println(emp1);
       }

}


Strategy 2By getters/setters

Create a class and exposing every property through setters/getters. Client will set the property using setter, access it via getters.

Pros:
1.    Easy to implement.
2.    In setters you can put validation or pass default value for optional parameters.

Cons:

1.    If object is share between multiple thread it can be happened that one thread just create the object and try to set the properties while other thread accesses this object, but as its properties have not been set yet wired exception can occur.


2.    As properties are exposed through setters/getters to make this object immutable need extra care.



Strategy 3. Using Varargs

Create a constructor like, Employee(Object… args) ,now client can pass variable length parameters. Constructor then checking the type of each parameter by instanceof operator.
If type is same as bean property set value else throw IllegalArgumentException.

Pros:
1.    No such.

Cons:

1.For each property you need to check type so it loses static type checking.
2. There is a conditional block for each property which increase cyclomatic complexity.



Strategy 4. Using Map

Same as varargs instead of varargs here we use a map.


Pros:
1.    No such.

Cons:

1.For each property you need to check type so it loses static type checking.
2. There is a conditional block for each property which increase cyclomatic complexity.



Example:

package com.example.builder;

import java.util.HashMap;
import java.util.Map;

public class EmployeeMap {
      
       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress ;
      
      
       EmployeeMap(Map<String,Object> map) throws IllegalArgumentException
       {
              if(!(map.containsKey("name") && map.containsKey("empId") && map.containsKey("company")))
              {
                     throw new IllegalArgumentException("Required  Parameter missing");
              }
              if((map.get("name")==null || map.get("empId")==null ||  map.get("company")==null))
              {
                     throw new IllegalArgumentException("Required  Parameter missing");
              }
             
              if(map.get("name") instanceof String)
              {
                     this.name =(String) map.get("name") ;
              }
              else{
                     throw new IllegalArgumentException("name Parameter  type is wrong");
              }
             
              if(map.get("empId") instanceof Integer)
              {
                     this.empId =(Integer) map.get("empId") ;
              }
              else{
                     throw new IllegalArgumentException("enpId Parameter  type is wrong");
              }
              if(map.get("company") instanceof String)
              {
                     this.company =(String) map.get("company") ;
              }
              else{
                     throw new IllegalArgumentException("company Parameter  type is wrong");
              }
             
             
              if(map.containsKey("passport") && (map.get("passport") instanceof Integer))
              {
                     this.passport = (Integer)map.get("passport");
              }
              else
              {
                     this.passport=0;
              }
             
              if(map.containsKey("tempAddress") && (map.get("tempAddress") instanceof String))
              {
                     this.tempAddress = (String)map.get("tempAddress");
              }
              else
              {
                     this.tempAddress="NA";
              }
       }
      
      
      
       @Override
       public String toString() {
              return "EmployeeMap [name=" + name + ", empId=" + empId + ", company="
                           + company + ", passport=" + passport + ", tempAddress="
                           + tempAddress + "]";
       }



       public static void main(String[] args) {
             
              try
              {
              Map map = new HashMap<String,Object>();
              map.put("name", "Shamik");
              map.put("empId", 100);
              map.put("company", "IBM");
              EmployeeMap emp = new EmployeeMap(map);
             
              Map map1 = new HashMap<String,Object>();
              map1.put("name", "Akash");
              map1.put("empId", 101);
              map1.put("company", "IBM");
              map1.put("passport", "1234");
              map1.put("tempAddress", "1,bangalore");
              EmployeeMap emp1 = new EmployeeMap(map1);
             
              System.out.println(emp);
              System.out.println(emp1);
             
              }
              catch(IllegalAccessException ex)
              {
                     ex.printStackTrace();
              }
       }

}
Strategy 5: Null values


Here client pass optional value as null and constructor checks if value is null then set default value for optional parameters, if parameter is required  and null ,constructor throws an exception.



Pros:
3.    For less number of parameters this is good way to design object.
4.    Very easy to implement.
Cons:
3.    For large number of parameters, it creates problems. Developer has to pass null value for optional parameters.
4.    If two adjacent parameters have same datatype and developer unintentionally swap values. Compiler not complain but it creates a genuine problem in runtime and very hard to track.


Example:

package com.example.builder;

public class EmployeeNull {
      
       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress;
      
      
       public EmployeeNull(String name,Integer empId,String company,Integer passport,String tempAddress) throws IllegalArgumentException
       {
              if(name ==null && empId==null && company==null)
              {
                     throw new IllegalArgumentException("Required  Parameter missing");
              }
             
              this.name=name;
              this.empId=empId;
              this.company=company;
              this.passport= passport != null?passport:0;
              this.tempAddress = tempAddress !=null? tempAddress:"NA";
       }


       @Override
       public String toString() {
              return "EmployeeNull [name=" + name + ", empId=" + empId + ", company="
                           + company + ", passport=" + passport + ", tempAddress="
                           + tempAddress + "]";
       }
      
      
       public static void main(String[] args) throws IllegalAccessException {
             
              EmployeeNull emp = new EmployeeNull("Shamik",100,"IBM",null,null);
              EmployeeNull emp1 = new EmployeeNull("Akash",101,"IBM",1234,"1,blore");
              System.out.println(emp);
              System.out.println(emp1);
             
       }
      
      
}


Strategy 6. Builder Pattern

Use a nested static class which act as builder of this bean. Builder takes require parameters as it’s constructor arguments and for each optional parameter there will be a helper method which set the value and return Builder instance itself. So we can invoke another helper method for parameter. Builder pattern maintains fluent interface pattern (Channing of methods).  At last build() method return an Immutable  bean object.

Pros:
1.    Immutability achieve easily so Object is thread safe by design.
2.    To set optional parameters, parameter position is not necessary as each parameter has helper method client can invokes them. It offers fluidity by chain of methods pattern.
Cons:
1.    Complex to implement

Example:

package com.example.builder;

public class Employee {
      
       private String name;
       private Integer empId;
       private String company;
       private Integer passport;
       private String tempAddress ;
      
       private Employee()
       {
             
       }
       private static class EmployeeBuilder
       {
              private String name;
              private Integer empId;
              private String company;
              private Integer passport;
              private String tempAddress ;
              public EmployeeBuilder(String name,Integer empId,String company)
              {
                     this.name=name;
                     this.empId=empId;
                     this.company=company;
              }
             
              public EmployeeBuilder setPassport(Integer passport)
              {
                     this.passport=passport;
                     return this;
                    
              }
             
              public EmployeeBuilder setTempAddress(String address)
              {
                     this.tempAddress=address;
                     return this;
                    
              }
             
              public Employee build()
              {
                     Employee emp = new Employee();
                    
                     emp.name=this.name;
                     emp.empId=this.empId;
                     emp.company=this.company;
                     emp.passport=this.passport!=null?this.passport:0;
                     emp.tempAddress=this.tempAddress!=null?this.tempAddress:"NA";
                     return emp;
              }

             
             
             
             
             
       }
      
      
      
       @Override
       public String toString() {
              return "Employee [name=" + name + ", empId=" + empId + ", company="
                           + company + ", passport=" + passport + ", tempAddress="
                           + tempAddress + "]";
       }



       public static void main(String[] args) {
             
              Employee emp = new Employee.EmployeeBuilder("Shamik", 100, "IBM").build();
              Employee emp1 = new Employee.EmployeeBuilder("Akash", 101, "IBM").setPassport(1234).setTempAddress("1,bangalore").build();
              System.out.println(emp);
              System.out.println(emp1);
       }
      

}





Comparison: Please find the picture below.


  
Conclusion:  For fewer parameters like 4 or less use Telescopic constructor pattern

For larger number of parameters use Builder pattern.


Post a Comment