Design Patterns - Factory Pattern

The factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.

Design Patterns - Factory Pattern
Photo by Jezael Melgoza / Unsplash

The series was off last week, so there's two posts to compensate. This week we will be going through the factory pattern and the singleton pattern. If you haven't checked out the other posts in the series I highly recommend you do.

The factory pattern isn't a design pattern as much as it is a part and parcel of the Java ecosystem. Let's have a look at the textbook definition from Wikipedia

In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.

Now that's a mouthful, so let's break it down piece by piece. The factory pattern is a creational pattern. So far we have seen several design patterns but those primarily deal with the organizational aspect of programming. The factory pattern primarily deals with the creation and instantiation of objects hence it's a creational design pattern.

A problem we have not yet dealt with in the previous posts in the series was the usage of new keyword. Despite trying to avoid concrete implementations, we were still instantiating a lot of them. The factor pattern aims to allay some of these concerns.

Finally, the last part of the definition talks about how we avoid burderning the constructor with a menagerie of optional configurations and instead creating a dedicated function for managing instantiation of the class based on certain parameters. But still it's probably hard to understand this from pure words, let's look at the actual code to understand this.

Traditional Approach

As has been customary throughout this series at this point, let's take an example that has to with cars.

public class CarDealer {
	//some code omitted
	Car sellCar(String type){
		Car car = null;

		if(type.equals("Toyota")){
			car = new Toyota();
		} else if(type.equals("Tesla")){
			car = new Tesla();
		} else if(type.equals("Hyundai")){
			car = new Hyundai();
		} else if(type.equals("Ford")){
			car = new Ford();
		} else if(type.equals("BMW")){
			car = new BMW();

		car.registerNumberPlate();
		car.repaint();
		car.completePaperwork();
        return car;
	}
}
Traditional Approach

Our CarDealer class here has a sellCar method that first creates a car variable and then has a lengthy if else statement to manage the creation of the specific brand of the car. Now this results in some problems for us.

  • What if the dealer starts selling a new kind of car, perhaps a car by Waymo, each change would require us to modify the sellCar function and ensure it stays upto date. Similarly, any removals would require us to remove the particular statment.
  • In addition, sellCar may not be the only function that requires creation of objects, perhaps there's another function designCar that requires the same object creation process. We will be dealing with a lot of duplicate code, any modifications would need to be replicated across the codebase.

So how do we deal with this? Enter the humble Factory.

Factory Approach

We need to identify the part of the code that is repetitive and can function independently.

if(type.equals("Toyota")){
	car = new Toyota();
} else if(type.equals("Tesla")){
	car = new Tesla();
} else if(type.equals("Hyundai")){
	car = new Hyundai();
} else if(type.equals("Ford")){
	car = new Ford();
} else if(type.equals("BMW")){
	car = new BMW();
}
Repeated part of the code

As we can see, this is the part of the code that deals exclusively with the creation of the object. We can create a new factory class called CarFactory.

public class CarFactory {
	Car createCar(String type){
		Car car = null;

		if(type.equals("Toyota")){
			car = new Toyota();
		} else if(type.equals("Tesla")){
			car = new Tesla();
		} else if(type.equals("Hyundai")){
			car = new Hyundai();
		} else if(type.equals("Ford")){
			car = new Ford();
		} else if(type.equals("BMW")){
			car = new BMW();
		}
		return car;
	}
}
CarFactory Class

And now we can just have the CarDealer class become a client of the CarFactory class for the generation of objects. We can add new car creation statements and remove or modify existing ones to this new class. Any changes we do in this single entity would reflect across any classes which are powered by this factory and we do not need to manually update every class. This fits right in with the Single Responsibility principle that we talked about in the Decorator Pattern post.

public class CarDealer {
	CarFactory factory;

	public CarDealer(CarFactory factory){
		this.factory = factory
	}

	Car sellCar(String type){
		Car car = null;
		
		car = factory.createCar(type);

		car.registerNumberPlate();
		car.repaint();
		car.completePaperwork();
	}
}
Updated CarDealer Class

In this new and updated version of the CarDealer class we have created a private member variable factory which is an instance of the CarFactory class. This allows us even more flexibility to switch the factories in the future if we ever need to. The instantiation car is dealt entirely by the factory and the sellCar method can continue to focus on the actual function it was designed for.

Closing Thoughts

As I mentioned before - there's two posts this week, make sure to check out the post detailing how to go about implementing the Singleton Pattern as well!

As always I would love to hear your feedback on this post, feel free to write to me at akshay [at] akshayprabhu [dot] dev or tweet at my twitter handle @akshaprabhu200.