Creational Pattern: Factory Method
This article discusses how derived classes can use their constructors to create appropriate objects.
What is it?
A software factory creates objects, and a factory producing real-world goods produces goods. For example, creating objects in Java usually happens like this:
The problem with that approach is that, suddenly, the code using the SomeClass object becomes dependent on the concrete implementation of SomeClass. There’s nothing wrong with new to create objects, but this approach can lead to tightly coupling our code to the concrete implementation class. This violates code-to-an-interface and not-to-an-implementation.
The factory method is a class design pattern that provides an interface for creating objects, but lets subclasses decide which class to instantiate.
Class Diagram
The class diagram includes the following elements:
- Product
- Concrete Product
- Creator
- Concrete Creator
Example
Continuing with our aircraft example, let’s assume we are trying to model the F-16 fighter jet. The client code needs to construct an engine object for this plane and fly it. A naive implementation for this class would be something like the below:
In the above code, we’ve committed ourselves to using a concrete implementation of the F16 class. What if the company comes up with newer versions of the aircraft and we are required to represent them in our program? That would mean changing the client code where we create an F16 instance. One way out is to encapsulate object creation in another object solely responsible for creating requested variants of the F-16. For starters, let’s say we want to represent the A and B variants of F16; our code might look like this:
A Simple Factory is a simple programming idiom that doesn’t create a factory object. The make method can be made static to avoid creating an unnecessary object. However, since static methods can’t be overridden in subclasses, we won’t be able to use this Simple Factory as the basis for an abstract class hierarchy.
However, we want to keep the creation of F16 object parts within the same class and still be able to introduce new F16 variants as they come along. In that case, we could subclass F16 and delegate the creation of the right F16 variant object to the subclass handling that variant. This is precisely how the factory method pattern works! The factory method here is makeF16() which will create a new F16 variant when it’s called. Proceeding forward, we introduce two subclasses like so
We subclassed and specialized the engine object by using inheritance. A factory method might or might not provide a default or generic implementation; however, we can still override it in subclasses to specialize or modify the product. Our example has two variant models, each with a different engine but the same cockpit. Client code can now use the newer models like so:
Note that the factory method design pattern returns an abstract type, either a Java interface or a Java abstract class. The superclass, in our case F16, doesn’t know what specific kind of F16 it was returned from the makeF16() method. In general, a create method is either abstract or comes with a default implementation invoked by the other superclass methods. So it’s up to subclasses to create specific kinds of objects.
Differences with Simple/Static Factory
The factory method pattern is similar to the simple or static factory, but differences include those simple factories can’t produce varying products through inheritance as a factory method pattern can.
Other Examples
The factory method pattern is pervasive in toolkits and frameworks. The pattern can be used whenever a class doesn’t know ahead of time what subclass objects it would need to instantiate. This often happens in framework design, where the consumers of the framework are expected to extend framework-provided abstract classes and hook-in functionality or object creations.
The Java API exposes several factory methods:
java.util.Calendar.getInstance()
java.util.ResourceBundle.getBundle()
java.text.NumberFormat.getInstance()
Caveats
- The pattern can result in having too many subclasses with only minor differences between them.
- If a subclass adds functionality to a superclass, it can’t use the new methods unless it downcasts the object to its concrete type. Unfortunately, Downcasting may fail at runtime.
Other articles in the Creational Pattern series
- Creational Pattern: Singleton
- Creational Pattern: Prototype
- Creational Pattern: Builder
- Creational Pattern: Abstract Factory
My other publications: https://ercindedeoglu.github.io/