Monday 28 November 2016

Why to use static factory methods over constructors

By using constructors, we can create objects to any class. There is also another way to create objects to a class. By using static factory methods, you can create instances to a class. Static factory method is a static method that returns instance of this class.

For example, following are some of the static factory methods provided by Java.

public static Integer valueOf(int i) //Return Integer object
public static Float valueOf(String s) throws NumberFormatException //Return Float object
public static Boolean valueOf(String s)//Return Boolean object

Let’s see the advantages of defining static factory methods.

a.   By providing the meaningful name to static factory method, we can give meaningful context to the developer (or) to the reader who is reading the source code.
b.   By using static factory methods we can control number of instances to a class
c.    Static factory methods provide flexibility that, it can return object of any subtype.

By providing the meaningful name to static factory method, we can give meaningful context to the developer (or) to the reader who is reading the source code.

Employee.java
public class Employee {
 private int id;
 private String firstName;
 private String lastName;
 private boolean permanentEmployee;

 private Employee(int id, String firstName, String lastName, boolean permanentEmployee) {
  this.id = id;
  this.firstName = firstName;
  this.lastName = lastName;
  this.permanentEmployee = permanentEmployee;
 }

 public boolean isPermanentEmployee() {
  return permanentEmployee;
 }

 public void setPermanentEmployee(boolean permanentEmployee) {
  this.permanentEmployee = permanentEmployee;
 }

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public static Employee getPermanentEmployee(int id, String firstName, String lastName) {
  return new Employee(id, firstName, lastName, true);
 }

 public static Employee getContractEmployee(int id, String firstName, String lastName) {
  return new Employee(id, firstName, lastName, false);
 }

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("Employee [id=").append(id).append(", firstName=").append(firstName).append(", lastName=")
    .append(lastName).append(", permanentEmployee=").append(permanentEmployee).append("]");
  return builder.toString();
 }

}

Notify above example, I provided two static factory methods to return permanent and contract employee instances. By looking at following method signatures, user can get an idea about the instance.

public static Employee getPermanentEmployee(int id, String firstName, String lastName);
public static Employee getContractEmployee(int id, String firstName, String lastName);

Test.java
public class Test {
 
 public static void main(String args[]){
  Employee permEmp = Employee.getPermanentEmployee(1, "Shanmukh", "Kummari");
  Employee contractEmp = Employee.getContractEmployee(2, "Hari Krishna", "Gurram");
  
  System.out.println(permEmp);
  System.out.println(contractEmp);
 }
 
}

Output
Employee [id=1, firstName=Shanmukh, lastName=Kummari, permanentEmployee=true]
Employee [id=2, firstName=Hari Krishna, lastName=Gurram, permanentEmployee=false]

By using static factory methods we can control number of instances to a class
By restricting access to the constructors of a class and by providing static factory method to get the instance, you can control the number of instances to the class at any given point of time.

Connection.java
public class Connection {
 private static Connection conn = new Connection();

 private Connection() {

 }

 public static Connection getConnection() {
  return conn;
 }

}

Test.java
public class Test {

 public static void main(String args[]) {
  Connection conn1 = Connection.getConnection();
  Connection conn2 = Connection.getConnection();

  System.out.println("(conn1 == conn2) : " + (conn1 == conn2));
  System.out.println("conn1.equals(conn2) : " + conn1.equals(conn2));
 }

}

Output
(conn1 == conn2) : true
conn1.equals(conn2) : true

Static factory methods provide flexibility that, it can return object of any subtype.

ShapeFactory.java
public class ShapeFactory {
 public interface Shape {
  public default void print() {
   System.out.println("Default implementaiton of shape");
  }
 }

 private static class Circle implements Shape {
  public void print() {
   System.out.println("I am inside circle");
  }
 }

 private static class Square implements Shape {
  public void print() {
   System.out.println("I am inside Square");
  }
 }

 private static class DefaultShape implements Shape {

 }

 public static Shape getShape(String shapeType) {
  switch (shapeType) {
  case "Square":
   return new Square();
  case "Circle":
   return new Circle();
  default:
   return new DefaultShape();
  }
 }
}

As you see 'ShapeFactory' class, it exposes the 'Shape' interface by making it as public and hides the implemebtations of Shape (Circle, Square, and DefaultShape) by making them as private.

Test.java
public class Test {

 public static void main(String args[]) {
  ShapeFactory.Shape shape1 = ShapeFactory.getShape("");
  ShapeFactory.Shape shape2 = ShapeFactory.getShape("Circle");
  ShapeFactory.Shape shape3 = ShapeFactory.getShape("Square");

  shape1.print();
  shape2.print();
  shape3.print();
 }

}

No comments:

Post a Comment