Static factory methods instead of constructors
The first item in the book “Effective Java” suggests using static factory methods instead of constructors. “But wouldn’t it make the class more complex than it should be?” I thought. After all, if you have a simple object with a single reason to change, a simple constructor would suffice. Well… We are talking about Java here where you can have multiple constructor definitions. On top of that, if your project is using a dependency injection framework like Guice, your constructor definitions are no longer yours to use. The composition of your objects is controlled by an Inversion of Control (IoC) container that can solve dependencies recursively using bindings. No manual dependencies allowed.
That’s why the static factory method suggestion intrigued me. I was trying to take control of my constructors back. While the IoC container handles the hard dependencies such as clients and services, I could utilize my manual dependencies through a static factory method. Neat. What happened at the end of reading the whole block of text about this item was certainly more than I could imagine. The reasons given by the author, Joshua Bloch, are quite convincing. So convincing that I could see myself using static factory methods over constructors (in certain cases) in other languages as well.
Static factory methods have names
Constructors are named the same as their classes. I think this is fine if you have a simple object to tinker with. However, if your domain object is intrinsically complex, then having multiple factory methods with proper names can be quite helpful.
public class Route {
// constructor
Route(Coordinate from, Coordinate to) {}
// static factory methods
public static Route newHighwayRoute(Coordinate from, Coordinate to) {}
public static Route newScenicRoute(Coordinate from, Coordinate to) {}
}
Class of the returned object can vary
The example above simply calls for polymorphism. The Route
class apparently encapsulates the logic of building a route from A to B. There are two main logic to create routes:
- A route that goes through the highway for fast journeys, and
- A route that goes through touristic areas for better scenery.
While preserving the Route
class, we can apply Strategy pattern to have scalable route creation logic. Then, static factory methods help us return object with differing types:
public interface Route {}
public class HighwayRoute implements Route {}
public class ScenicRoute implements Route {}
public class RouteGenerator {
// constructor
RouteGenerator(Coordinate from, Coordinate to) {}
// static factory methods
public static HighwayRoute newHighwayRoute(Coordinate from, Coordinate to) {}
public static ScenicRoute newScenicRoute(Coordinate from, Coordinate to) {}
}
Class of the returned object does not have to exist
Let’s assume that we decided to create another strategy for generating routes: Toll free. We’d like to offer the option of rather a fast journey like highways but without the need of paying.
In this case, we can easily create a new static factory method for immediate API access to the user. We don’t have to have a TollFreeRoute
implementation yet.
public interface Route {}
public class HighwayRoute implements Route {}
public class ScenicRoute implements Route {}
public class RouteGenerator {
// constructor
RouteGenerator(Coordinate from, Coordinate to) {}
// static factory methods
public static HighwayRoute newHighwayRoute(Coordinate from, Coordinate to) {}
public static ScenicRoute newScenicRoute(Coordinate from, Coordinate to) {}
public static Route newTollFreeRoute(Coordinate from, Coordinate to) {}
}
Static factory methods can return subtypes
Well, that is obvious if you’ve followed the polymorphism example. Normally, the examples above should have been more strictly related to inheritance than polymorphism. Because if we continue working on the example above, the next logical step would be to strip Route
logic completely from RouteGenerator
and actually use RouteGenerator
’s constructor:
public interface Route {}
public class HighwayRoute implements Route {}
public class ScenicRoute implements Route {}
public class TollFreeRoute implements Route {}
public class RouteGenerator {
// constructor
RouteGenerator(Coordinate from, Coordinate to) {}
// generators
public HighwayRoute getHighwayRoute() {}
public ScenicRoute getScenicRoute() {}
public TollFreeRoute getTollFreeRoute() {}
}
However, let’s reimagine our example with another interface: TravelMethod
and have our Route
class return itself based on the method of travel:
public interface TravelMethod {}
public class Route {
// constructor
Route(TravelMethod method) {}
}
There are different types of travel methods such as driving, walking, or public transportation. So for each and every different type, our Route
implementation should construct a route. How do you differ the constructed objects? Well, perhaps we can power our Route
class with multiple constructors?
public interface TravelMethod {}
public class Walking implements TravelMethod {}
public class Driving implements TravelMethod {}
public class PublicTransportation implements TravelMethod {}
public class Route {
// constructors
Route(Walking method) {}
Route(Driving method) {}
Route(PublicTransportation method) {}
}
For each type of travel method, we have a concrete Route
object constructed. However, we can do better here if we use static factory methods.
public interface TravelMethod {}
public class Walking implements TravelMethod {}
public class Driving implements TravelMethod {}
public class PublicTransportation implements TravelMethod {}
public class Route {
// static factory methods
public static Route newWalkingRoute(Walking method) {}
public static Route newDrivingRoute(Driving method) {}
public static Route newPublicTransportationRoute(PublicTransportation method) {}
}
Here, we have named factory methods to differentiate between the travel methods. However, we can also invert the logic here and instead of returning a Route
based on the travel method, we can return a differing type of Route
based on the given implementation of TravelMethod
interface.
public interface TravelMethod {}
public class Walking implements TravelMethod {}
public class Driving implements TravelMethod {}
public class PublicTransportation implements TravelMethod {}
public class WalkingRoute extends Route {}
public class DrivingRoute extends Route {}
public class PublicTransportationRoute extends Route {}
public abstract class Route {
// static factory method
public static Route newRoute(TravelMethod method) {}
}
Here, Route
and TravelMethod
have started communicating through abstractions. The abstract Route
class accepts any TravelMethod
implementation in its static factory method and returns a subtype of Route
based on the encapsulated logic inside.
Static factory methods can return existing objects instead of instantiating new ones
Let’s revisit the RouteGenerator
example from above with a bit of a twist:
public class RouteGenerator {
RouteGenerator(Coordinate from, Coordinate to) {}
}
Imagine we are processing an expensive computation within the RouteGenerator
class constructor. What happens when you’d like to get another RouteGenerator
with the same coordinates? You have a few options:
- You can create a new instance by going through the constructor. This will be another expensive operation.
- You could cache the old
RouteGenerator
object beforehand and hit the cache next time you need it. - You can have a manager class to have strict control over instances.
When you followed the third approach, you’d actually cover all those options in a best case scenario:
public class RouteManager {
private HashMap<Coordinates, Route> routes = new HashMap<>();
public static Route getRoute(Coordinate from, Coordinate to) {}
}
Here, our RouteManager
controls the creation of Route
instances. When RouteManager.newRoute(from, to)
is called for the first time, the expensive operation is processed and it’s cached in the instance variable routes
. Next time you call RouteManager.newRoute(from, to)
with the same arguments, it’s going to be served from the routes
variable instead of creating the same Route
again.
This approach can be globalized with Singleton pattern where you give the instance control to its own class. However, I wanted to keep the example simple without going into Singleton because it’s a controversial pattern.
Limitations of static factory methods
According to Joshua Bloch, there are two main limitations of using static factory methods. First, they are not as easy as finding constructors in the API documentation since they are not treated as constructors. The second one is that classes without non-private constructors cannot be subclassed. This may be a good side-effect, though, as Bloch also states, because one will have to use composition over inheritance.
Some of the examples above can also be achieved in simple compositional logic. Static factory method is a shiny name to decouple construction logic away from the class itself. However, you can achieve the similar or even better results by refactoring into deeper models. So you don’t have to go apply static factory methods everywhere instead of using the core principles such as composition, polymorphism, and KISS. But I’m sold on the idea of using them where it’s not easy or simple to encapsulate the domain logic with plain objects. Heck, I’m even getting used to using Singleton objects but that’s for another post.