I believe Joshua Bloch said it first in his very good book "Effective Java": static factory methods are the preferred way to instantiate objects compared with constructors. I disagree. Not only because I believe that static methods are pure evil, but mostly because in this particular case they pretend to be good and make us think that we have to love them.
Let's analyze the reasoning and see why it's wrong, from an object-oriented point of view.
This is a class with one primary and two secondary constructors:
This is a similar class with three static factory methods:
Which one do you like better?
According to Joshua Bloch, there are three basic advantages to using static factory methods instead of constructors (there are actually four, but the fourth one is not applicable to Java anymore):
- They have names.
- They can cache.
- They can subtype.
I believe that all three make perfect sense ... if the design is wrong. They are good excuses for workarounds. Let's take them one by one.
They Have Names
This is how you make a red tomato color object with a constructor:
This is how you do it with a static factory method:
It seems that
makeFromPalette() is semantically richer than just
new Color(), right? Well, yes. Who knows what those three numbers mean if we just pass them to the constructor. But the word "palette" helps us figure everything out immediately.
However, the right solution would be to use polymorphism and encapsulation, to decompose the problem into a few semantically rich classes:
Now, we use the right constructor of the right class:
They Can Cache
Let's say I need a red tomato color in multiple places in the application:
Two objects will be created, which is obviously inefficient, since they are identical. It would be better to keep the first instance somewhere in memory and return it when the second call arrives. Static factory methods make it possible to solve this very problem:
Then somewhere inside the
Color we keep a private static
Map with all the objects already instantiated:
It is very effective performance-wise. With a small object like our
Color the problem may not be so obvious, but when objects are bigger, their instantiation and garbage collection may waste a lot of time.
However, there is an object-oriented way to solve this problem. We just introduce a new class
Palette, which becomes a store of colors:
Now, we make an instance of
Palette once and ask it to return a color to us every time we need it:
See, Joshua, no static methods, no static attributes.
They Can Subtype
Let's say our class
Color has a method
lighter(), which is supposed to shift the color to the next available lighter one:
However, sometimes it's more desirable to pick the next lighter color through a set of available Pantone colors:
Then, we create a static factory method, which will decide which
Color implementation is the most suitable for us:
If the true red color is requested, we return an instance of
PantoneColor. In all other cases it's just a standard
RGBColor. The decision is made by the static factory method. This is how we will call it:
It would not be possible to do the same "forking" with a constructor, since it can only return the class it is declared in. A static method has all the necessary freedom to return any subtype of
However, in an object-oriented world we can and must do it all differently. First, we would make
Color an interface:
Next, we would move this decision making process to its own class
Colors, just like we did in the previous example:
And we would use an instance of class
Colors instead of a static faсtory method inside
However, this is still not really an object-oriented way of thinking, because we're taking the decision-making away from the object it belongs to. Either through a static factory method
make() or a new class
Colors—it doesn't really matter how—we tear our objects into two pieces. The first piece is the object itself and the second one is the decision making algorithm that stays somewhere else.
A much more object-oriented design would be to put the logic into an object of class
PantoneColor which would decorate the original
Then, we make an instance of
RGBColor and decorate it with
red to return a lighter color and it returns the one from the Pantone palette, not the one that is merely lighter in RGB coordinates:
Of course, this example is rather primitive and needs further improvement if we really want it to be applicable to all Pantone colors, but I hope you get the idea. The logic must stay inside the class, not somewhere outside, not in static factory methods or even in some other supplementary class. I'm talking about the logic that belongs to this particular class, of course. If it's something related to the management of class instances, then there can be containers and stores, just like in the previous example above.
To summarize, I would strongly recommend you never use static methods, especially when they are going to replace object constructors. Giving birth to an object through its constructor is the most "sacred" moment in any object-oriented software, don't miss the beauty of it.