Thursday, September 17, 2015

Effective Java - Item 1 : Using static factory methods as an alternative to constructors

My first code blog. A review and my understanding of the book Effective Java by Joshua Bloch.

The book as it's name suggests focuses on the effective use of Java. It contains 78 items each of which capture the best practices to be followed. It contains code examples illustrating many design patterns and idioms. I have tried to capture the essence of the items by giving my own examples in this blog.

This book is all about writing programs that are clear, correct, usable, robust, flexible and maintainable.

Chapter 1 : Creating and destroying objects

We all know that objects are the core of Java. But we need to be very miserly when creating objects. Creating a lot of unnecessary objects leads to various issues like unclean code and low performance.
Under this chapter author gives 7 suggestions for better ways to create objects, maintain them and carefully destroy them.

Item 1 : Using static factory methods as an alternative to constructors.

Static factory method is one which returns an instance of the class. Here we need to note the fact that it just returns an instance and does not create a new object every time it is invoked unlike in a constructor. 
Let me explain the motivation behind this item with an example. 
Consider the class RandomIntGenerator. As its name suggests it is used to generate random integers.
1
2
3
4
5
6
 public class RandomIntGenerator {
    private final int min;
    private final int max;
 
    public int next() {...}
 }

It has two attributes min and max and a random integer between these 2 values is generated. These values have to be initialized in the constructor since they are declared as final so we have the following constructor.
1
2
3
4
 public RandomIntGenerator(int min, int max) {
    this.min = min;
    this.max = max;
 }
Now what if you only had a value for min and you wanted a random integer between that value and the highest possible value for integers? We can have another constructor as follows:
1
2
3
4
 public RandomIntGenerator(int min) {
    this.min = min;
    this.max = Integer.MAX_VALUE;
 }
Similarly what if you only had a value for max and wanted an integer between that value and the smallest possible integer? You might think of having a third constructor as follows:
1
2
3
4
 public RandomIntGenerator(int max) {
    this.min = Integer.MIN_VALUE;
    this.max = max;
 }
But we will get a compilation error saying Duplicate method because there can be only one constructor with a particular signature. 
So here is where static factory methods come into picture.
In each of the above 3 cases we can have static factory methods to create an instance of the RandomIntGenerator class as follows:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 public class RandomIntGenerator {
    private final int min;
    private final int max;
 
    private RandomIntGenerator(int min, int max) {
        this.min = min;
        this.max = max;
    }
     
    public static RandomIntGenerator between(int max, int min) {
        return new RandomIntGenerator(min, max);
    }
     
    public static RandomIntGenerator biggerThan(int min) {
        return new RandomIntGenerator(min, Integer.MAX_VALUE);
    }
     
    public static RandomIntGenerator smallerThan(int max) {
        return new RandomIntGenerator(Integer.MIN_VALUE, max);
    }
 
    public int next() {...}
 }
As we can see static factory methods have very clear names which helps anyone reading the code to understand why it is used. Also by making the constructor private we are ensuring that no unnecessary instantiations have been done.

Continuing with our example, now let us suppose we want random generators for other datatypes as well. So a good solution would be to have an interface.
1
2
3
 public interface RandomGenerator<T> {
    T next();
 }
So now our implementation of RandomIntGenerator changes as follows
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 class RandomIntGenerator implements RandomGenerator<Integer> {
    private final int min;
    private final int max;
 
    RandomIntGenerator(int min, int max) {
        this.min = min;
        this.max = max;
    }
 
    public Integer next() {...}
 }
Similarly we can have a random string generator as follows
1
2
3
4
5
6
7
8
9
 class RandomStringGenerator implements RandomGenerator<String> {
    private final String prefix;
 
    RandomStringGenerator(String prefix) {
        this.prefix = prefix;
    }
 
    public String next() {...}
 }
Here notice that we haven't used the public keyword to define the above classes. So it will be package-private by default and this makes it impossible for any client outside of their package to instantiate these generators. 
Once again we have static factory methods to the rescue. We can provide public static factory methods to create instances of the above generators and bundle all these into a utility class. Since it is a utility class and it does not make sense to instantiate it we can make the constructor private.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 public final class RandomGenerators {
    // Suppresses default constructor, ensuring non-instantiability.
    private RandomGenerators() {}
     
    public static final RandomGenerator<Integer> getIntGenerator() {
        return new RandomIntGenerator(Integer.MIN_VALUE, Integer.MAX_VALUE);
    }
     
    public static final RandomGenerator<String> getStringGenerator() {
        return new RandomStringGenerator("");
    }
 }



So the main idea here is that the implementation classes are hidden and the clients interact only through the interface. This allows a lot of flexibility to add new implementations or change existing ones without the clients noticing the change.

But is it really an all pros and no cons scenario? Not exactly.
Since we are suppressing the constructors by making it private we cannot extend them. But then again we can always use composition which many Java authors suggest is better than using inheritance. 
Another problem might have to do with readability or more so with familiarity to the traditional way of creating instances using the new() keyword. But just like any other practice it will take some time to get adapted.
In summary it is always a good habit to give preference to static factory methods over public constructors.

No comments:

Post a Comment