Learn the Factory Design Pattern
This is the 3rd post in a series on design patterns.
The factory method is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.
Suppose you’re building a travel management app. Your first version can only handle car travel, so the majority of your code lives in the Car class. It becomes quite popular after a while. Every day, you receive many requests from airline companies to include domestic and overseas flights.
The app is great, but since most of the code is tightly coupled to the Car class, adding a Flight class would require making changes to all of the app’s code. Further, in case you decide to add another type of transportation, such as sea travel, you will probably have to make all of these changes again. As a result, you’ll end up with pretty nasty code, riddled with conditionals that switch the app’s behavior based on the class of transportation objects.
Factory Method Design Pattern
The factory method pattern suggests that you replace direct object construction calls with calls to a factory method. Objects returned by factory methods are called products.
By overriding the factory method in a subclass, we can change the type of product that is being created. The factory method in the base class should have this interface declared as its return type.
Both Car and Flight classes implement the Transport interface, which declares a method called deliver(). Each class implements the method differently, car takes passengers by road and flight takes them by air.
Depending on client requirements, the ConcreteTransportationFactory class returns car or flight objects.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
Implementation steps:
- Make all products follow the same interface, which is common to all objects created by the creator and its subclasses.
- Create a factory method in the creator class. Its return type should match the common product interface.
- Override the factory method in the subclasses and extract the appropriate bits of construction code for each product.
Source Code Implementation
We begin by implementing a Transport interface with a single method, travel().
The Car class implements the Transport interface and overrides the travel() method and output text that’s specific to its travel mode.
The Flight class implements the Transport interface and overrides the travel() method and output text that’s specific to its travel mode.
Enum to select transport mode.
This code segment represents the abstract class TransportationFactory.
In the following code, ConcreteTransportationFactory extends TransportationFactory. Based on the enum argument, this class determines what transport type should be instantiated.
The final class in our implementation is FactoryMethodClient. It contains the main() method and drives the application. The main() method creates a ConcreteTransportationFactory with two Transport instances, one for each transport mode type.
When To Apply Factory Method Design Pattern
- This design pattern is useful when you don’t know in advance the exact types and dependencies of the objects your code must work with. It allows you to separate the code that constructs a product from the code that uses it.
- When you want your library’s users to be able to extend its internal components, use the factory method design pattern. It can be achieved by reducing the code that constructs components across the framework into a single factory method, which can then be overridden by anyone.
- When you want to save system resources by reusing existing objects rather than rebuilding them every time, use the factory method design pattern.
Pros of Factory Method Design Pattern
- Avoid tight coupling between the creator and the concrete products.
- Implement the single responsibility principle by consolidating the product creation code.
- You can introduce new products into the software without breaking existing client code by using the Open/Closed Principle.