The common way to create objects in
Java is by using public constructors. A class provides public constructor e.g.
java.lang.String so anyone can create an instance of String class to use in
their application.
But, there is
another technique which can be used to create objects in Java and every experienced Java programmer should know about it. A class
can provide a public static factory method which can return an instance of the
class e.g. HashMap.newInstance(). The factory method is a smart way to create
objects in Java and provides several advantages over the traditional approach
of creating objects using constructors in Java. It can also improve the quality
of code by making the code more readable, less coupled, and improves
performance by caching.
Constructor vs Factory Method in Java
The
problem we saw in the previous paragraph is just one of the many problems you
face when you decide to provide a public constructor for creating an instance
of the class, you will learn more of such shortcomings of constructor in Java
as we discuss actual differences one by one. Thankfully, Factory methods
address many limitations of constructors in Java and help to write cleaner
code.
Here is my list of some key differences between constructor and static factory method in Java. All these differences stem from the shortcoming of constructors and explain how static factory methods solves those problems. They will also explain relative pros and cons of different ways of creating objects in Java.
Here is my list of some key differences between constructor and static factory method in Java. All these differences stem from the shortcoming of constructors and explain how static factory methods solves those problems. They will also explain relative pros and cons of different ways of creating objects in Java.
Readable
Names
One of the serious limitation of the constructor is that you cannot give it an explicit name, the name must be same as the name of the class. If your class return two different types of object for a different purpose, you can use factory methods with more readable names.
A good example of this concept is java.text.NumberFormat class in JDK, which provides different factory methods to returns different objects e.g. getCurrencyInstance() to return an instance of NumberFormat which can format currency, getPercentInstance() to return a percentage format, and getNumberInstance() to return a general purpose number formatting.
If you have used new NumberFormat(), it would have been difficult to know that which kind of NumberFormat instance would have returned.
Polymorphism
Another serious limitation of a constructor is that it always return the same type of object. You cannot vary the type of constructed object, it must be same as the name of the contractor. But the factory method can return an object of different subclasses e.g. in above example, if you call getNumberInstance() then it return an object of DecimalFormat class.
The Polymorphism also allows static factory methods to return instances of non-public classes e.g. RegularEnumSet and JumboEnumSet in the case of EnumSet interface. When you call the EnumSet.of() method, which is a static factory method, it can return an instance of either of this class depending upon the number of enum constants in the Enum class you have provided.
One of the serious limitation of the constructor is that you cannot give it an explicit name, the name must be same as the name of the class. If your class return two different types of object for a different purpose, you can use factory methods with more readable names.
A good example of this concept is java.text.NumberFormat class in JDK, which provides different factory methods to returns different objects e.g. getCurrencyInstance() to return an instance of NumberFormat which can format currency, getPercentInstance() to return a percentage format, and getNumberInstance() to return a general purpose number formatting.
If you have used new NumberFormat(), it would have been difficult to know that which kind of NumberFormat instance would have returned.
Polymorphism
Another serious limitation of a constructor is that it always return the same type of object. You cannot vary the type of constructed object, it must be same as the name of the contractor. But the factory method can return an object of different subclasses e.g. in above example, if you call getNumberInstance() then it return an object of DecimalFormat class.
The Polymorphism also allows static factory methods to return instances of non-public classes e.g. RegularEnumSet and JumboEnumSet in the case of EnumSet interface. When you call the EnumSet.of() method, which is a static factory method, it can return an instance of either of this class depending upon the number of enum constants in the Enum class you have provided.
Coupling
Factory methods promote the idea of coding using Interface then implementation which results in more flexible code, but constructor ties your code to a particular implementation.
On the other hand by using constructor you tightly any client (who uses that class) to the class itself. Any change in class e.g. introducing a new constructor or new implementation will require change almost everywhere.
Factory methods promote the idea of coding using Interface then implementation which results in more flexible code, but constructor ties your code to a particular implementation.
On the other hand by using constructor you tightly any client (who uses that class) to the class itself. Any change in class e.g. introducing a new constructor or new implementation will require change almost everywhere.
Type
Inference
Until Java 7, the constructor doesn't provide automatic type inference, which means you need to declare generic types on both left and right side of variable declaration as shown below.
This results in unreadable and cluttered code. Java 7 improved this by introducing the diamond operator, which helps to infer types, but factory method has this type inference right from Java 1.5. Google Guava has created several static utility classes with lots of factory methods to take advantage of improved type inference provided by factory methods. here are a couple of examples.
Caching
A constructor always creates a new object in heap. It's not possible to return a cached instance of a class from Constructor. On other hand, Factory methods can take advantage of caching. It's, in fact, a common practice to return the same instance of Immutable classes from factory method instead of always creating a new one.
Until Java 7, the constructor doesn't provide automatic type inference, which means you need to declare generic types on both left and right side of variable declaration as shown below.
This results in unreadable and cluttered code. Java 7 improved this by introducing the diamond operator, which helps to infer types, but factory method has this type inference right from Java 1.5. Google Guava has created several static utility classes with lots of factory methods to take advantage of improved type inference provided by factory methods. here are a couple of examples.
Caching
A constructor always creates a new object in heap. It's not possible to return a cached instance of a class from Constructor. On other hand, Factory methods can take advantage of caching. It's, in fact, a common practice to return the same instance of Immutable classes from factory method instead of always creating a new one.
In summary, both static factory methods and constructor have their usage and it's important for an experienced developer to understand their relative merits. In most cases, static factories are better choices so avoid the nature of providing public constructors without first considering static factories for creating objects.
No comments:
Post a Comment