The common way to create objects in Java is by using public constructors. A class provides a public constructor like java.lang.String so anyone can create an instance of String class to use in their application, but, there is another technique that 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 that 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.
Let's analyze some difference between constructor and factory method to find out which one is better for creating objects. But before that, let's find learn what is factory method and what are shortcomings of constructors in Java?
A factory method is nothing but a static method that returns an object of the class. The most common example of a factory method is getInstance() method of any Singleton class, which many of you have already used.
The main problem with a constructor is that by definition they don't have names. As such, a class can have only one constructor with a given signature, which limits your capability to provide a more useful constructor.
Let's analyze some difference between constructor and factory method to find out which one is better for creating objects. But before that, let's find learn what is factory method and what are shortcomings of constructors in Java?
A factory method is nothing but a static method that returns an object of the class. The most common example of a factory method is getInstance() method of any Singleton class, which many of you have already used.
The main problem with a constructor is that by definition they don't have names. As such, a class can have only one constructor with a given signature, which limits your capability to provide a more useful constructor.
For example, if you already have a constructor IdGenerator(int max) which generates integer Id up to that number and you want to add IdGenerator(int min) you cannot do that, the compiler will throw an error.
Btw, If you are serious about learning design patterns and principles, I suggest you take a look at the Design Patterns in Java course on Udemy. This course covers both SOLID design principles like Open Closed and Liskov substitution, and all-important Object Oriented design patterns like Decorator, Observer, Chain of Responsibility, and much more.
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.
Btw, If you are serious about learning design patterns and principles, I suggest you take a look at the Design Patterns in Java course on Udemy. This course covers both SOLID design principles like Open Closed and Liskov substitution, and all-important Object Oriented design patterns like Decorator, Observer, Chain of Responsibility, and much more.
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.
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.
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.
This means, most suitable class is used for the job. This improves the performance of your Java code as well. You can further read, Java Performance The Definitive Guide By Scott Oaks to learn more about such specialized trick to improve performance.
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.
Hence, it's very difficult to change and unit test the code which uses constructors to create objects all over.
It's also one of the best practice in Java to make the constructor private to ensure that objects are only created using public static factory methods provided. The standard JDK API uses this technique with many new classes e.g. EnumSet.
If you have ever used EnumSet then you might know that you cannot create instances of this class using a constructor, you must use the static factory method, EnumSet.of() to create an instance. This allows JDK to use choose the more relevant implementation of EnumSet depending upon the key sizes of provided Enum object. See RegularEnumSet vs JumboEnumSet to learn more about how EnumSet works in Java.
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.
For example Integer.valueOf(), Long.valueOf() are factory methods to create instance of Integer and Long respectively and return cached value in range of -128 to 127.
They are also used during auto-boxing. Since we mostly used values in that range, it greatly improves performance by reducing memory consumption and pressure on garbage collection. You can read Effective Java Item 1 to learn more about how caching of valueOf() method works. The item is actually also the best source to learn why static factory methods are better than constructor for creating objects.
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.
That's all about the difference between factory method and constructor in Java. You can see that factory method provides several advantages over constructor and should be preferred for object creation 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.
1. 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.
2. 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.
This means, most suitable class is used for the job. This improves the performance of your Java code as well. You can further read, Java Performance The Definitive Guide By Scott Oaks to learn more about such specialized trick to improve performance.
3. 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.
Hence, it's very difficult to change and unit test the code which uses constructors to create objects all over.
It's also one of the best practice in Java to make the constructor private to ensure that objects are only created using public static factory methods provided. The standard JDK API uses this technique with many new classes e.g. EnumSet.
If you have ever used EnumSet then you might know that you cannot create instances of this class using a constructor, you must use the static factory method, EnumSet.of() to create an instance. This allows JDK to use choose the more relevant implementation of EnumSet depending upon the key sizes of provided Enum object. See RegularEnumSet vs JumboEnumSet to learn more about how EnumSet works in Java.
4. 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.
5. 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.For example Integer.valueOf(), Long.valueOf() are factory methods to create instance of Integer and Long respectively and return cached value in range of -128 to 127.
They are also used during auto-boxing. Since we mostly used values in that range, it greatly improves performance by reducing memory consumption and pressure on garbage collection. You can read Effective Java Item 1 to learn more about how caching of valueOf() method works. The item is actually also the best source to learn why static factory methods are better than constructor for creating objects.
Disadvantages of Static Factory methods
Like all things in the world, static factor methods also has some disadvantages e.g. once you make the constructor private to ensure that the class can only be instantiated using the constructor, you lost the power of inheritance because classes without public or protected constructor cannot be extended. But, if you look deep, this is not such a bad thing because it encourages you to favor Composition over Inheritance for code reuse, which results in more robust and flexible software.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.
That's all about the difference between factory method and constructor in Java. You can see that factory method provides several advantages over constructor and should be preferred for object creation in Java.
In fact, this is advised by the great Java developer and author of popular Java APIs like Collection framework and several useful classes of java.lang package, Joshua Bloch on his all-time classic Java book, Effective Java. You can read the very first item from this book to learn more about why factory method is better than constructor in Java.
Other Java and Programming Resources you may like
Thanks for reading this article. If you find these differences between the constructor and static factor method patterns in Java useful, then please share them with your friends and colleagues. If you have any questions or feedback, then please drop a note.
Other Java and Programming Resources you may like
- The Java Developer RoadMap
- 10 Things Java Programmer should learn
- 10 Books Every Programmer Must Read
- 10 Tools Every Software Developer should know
- 21 Tech skills Java developers should learn
- 5 Courses to Learn Software Architecture in Depth
- 10 Courses to learn DevOps in Depth
- My favorite courses to learn Software Architecture
- 10 Tips to Improve Your Programming skill
- 20 Libraries and APIS Java Programmer Should Know
- 7 Best books to learn about Design Patterns
- 10 Framework and Library Java and Web Developer Should Learn
Thanks for reading this article. If you find these differences between the constructor and static factor method patterns in Java useful, then please share them with your friends and colleagues. If you have any questions or feedback, then please drop a note.
1 comment :
Good stuff..
Post a Comment