All variations of the Factory Design Pattern — A simple and easy guide
Introduction and expectations from this article
Hey guys! I hope you are doing well and learning well.
In this article, I will cover the famous Factory Design Pattern and its variations which are heavily used in Software Engineering. My target is to cover this article and subsequent design pattern articles in a clear and crisp manner without including any unnecessary details.
I have NOT included any class diagrams in this article. Rather, I have tried to explain the concepts in a step-by-step manner and in very simple language without including much technical jargon. Hence, it can be used by a beginner as well as seasoned developers looking for a revision.
I have used small code samples of Java to solidify your understanding. Even if you are not a Java person, I think you should still go through this article as the core knowledge of design patterns remains the same and is independent of the programming language.
Prerequisite: Basic understanding of Object Oriented Programming
Before you go: This article might seem a long one to you because it contains all 3 variations of the factory design pattern, but I have ensured to write it in very simple and easy-to-understand language. Hence, please have some patience while reading this article, and please go through the complete content on this page. Leaving it in the mid won’t help you.
Please don’t forget to add your comments after reading this article so that I can be aware of any scope for improvement in this article.
Let’s begin!
What is Factory Design Pattern?
The factory design pattern is one of the Creational design patterns. As the name suggests, it acts like a factory of objects, which means it supplies us with the required objects whenever we need to create one.
We, software engineers, are lazy, aren’t we? Finding the right object to instantiate and then instantiating that object with required parameters/arguments at every place we need that object 😅— man! that is a tough task. Wouldn’t it be nice if someone do this hard work of creating the right object for us 🤩? Well, that’s what the factory design pattern does! It makes our lives easier in many aspects.
Taking a step back, the creational design patterns are used to create “Objects”. Using the creational design patterns, we ensure that the Object creation logic is segregated away so that the clients of the code do not need to know how exactly the object is being created and what class is exactly being instantiated (😎 remember the concept of abstraction ?).
Variations of the Factory Design Pattern
You may have seen multiple variations of the Factory design pattern. If you have not already seen them or are confused — no worries! Here you go:
- Simple factory pattern
- Factory method pattern
- Abstract factory pattern
Let’s go through each of them one by one in the above-mentioned order for better understanding.
[Variation 1] Simple factory pattern
- It is the most basic form of the factory design pattern
- To implement this we create a separate class called the factory class. The factory class is responsible for deciding which object to create based on input or other factors.
Let’s see a simple code for this for better understanding:
Let’s suppose we want to create two classes — Dog
and Cat
. These are of type Animal
, right? So, let’s create an interface for Animal
(we can create an abstract class too, but I am going ahead with the interface)
public interface Animal {
void speak();
}
public class Dog implements Animal {
@Override
public void speak() {
System.out.println("Woof woof");
}
}
public class Cat implements Animal {
@Override
public void speak() {
System.out.println("Meow meow");
}
}
Okay, so now we have our classes with us. Now let’s imagine we want to create an object for these classes using the factory class as explained above. Let’s name our factory class — AnimalFactory
and let’s create that factory class:
public class AnimalFactory {
public static Animal createAnimalObject(String animalType) {
if ("Dog".equals(animalType)) {
return new Dog();
}
else if ("Cat".equals(animalType)) {
return new Cat();
}
else {
throw new IllegalArgumentException("Invalid animal type");
}
}
}
This AnimalFactory
class has a static method that we can call by passing the “animalType” argument. This “animalType” indicates the name of the class for which we need to create an object. For example — if we pass “Dog” to it then the Dog
class that we created before will be initialized and the corresponding object will be returned to the caller of this method.
Let’s see how the object will be created by the clients in the client application (the client application can be any application or any other class which is using our factory class to get the required objects):
// We should always code to an interface and not to an implementation
// Here, coding to the interface 'Animal' and not the implementation 'Dog'
// For more clarity - LHS below is [Animal animal] and not [Dog animal]
Animal animal = AnimalFactory.createAnimal("Dog"); // this will return the object for Dog class
animal.speak(); // Output: Woof woof
Now you must be wondering what we have essentially achieved doing this. Here you go:
- It segregated away object creation logic to the factory class. Hence, we as clients of this factory class or any other clients need not know the exact instantiation process. This might seem to make less sense for our example provided above as we have very simple classes with no arguments in the constructor of those classes, thus making it super easy to instantiate them.
If you too think the same, just imagine the usefulness of this when we have hundreds of classes in a production-grade application with varying types and numbers of arguments in the constructor of classes. In such scenarios, it is wise to take help from the factory class(es). - It allowed us to create objects in a flexible and extensible way, without having to hard-code the object creation logic in our client application.
That’s it. We are done with this variation of the factory design pattern.
But, wait a minute…🧐
Have you heard about the “Open Closed Principle” (hereafter referred to as OCP in this article) or the “O” of the SOLID design principles? The OCP states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. In other words, you should be able to add new functionality to a software entity without changing its existing code.
So, I have a question for you — By looking at the AnimalFactory
class above, do you feel the OCP is violated? Think think …. 🤔 💭 🧐
If your answer is yes, then it is correct. Let me elaborate on why the AnimalFactory
class violates the OCP. We have the if-else branching in createAnimalObject()
method for the type of object we need. Suppose in the future, we create a third type of Animal
. In such a case we will be needing to modify the logic/code of createAnimalObject()
method in AnimalFactory
class to include this new type, right? Hence, in this case, due to this modification of existing logic written in createAnimalObject()
method, it will violate the OCP.
So, how to fix this 🤔 ? One way to avoid violating the OCP when using the factory design pattern is to use a technique called “reflection.” Reflection is a feature of some programming languages (for example — Java) that allows a program to inspect and modify its own structure and behavior at runtime.
Following is an example code in Java that I am adding for your reference. This might seem a little complex to you if you are not familiar with reflection, but believe me, it is not! Read it with patience. Here you go:
public interface Animal {
void speak();
}
public class Dog implements Animal {
// Adding nickname to Dog for the sake of new example
private String nickname;
// Constructor
public Dog(String nickname) {
this.nickname = nickname;
}
@Override
public void speak() {
System.out.println("Woof woof! My nickname is: " + this.nickname);
}
}
public class Cat implements Animal {
@Override
public void speak() {
System.out.println("Meow meow");
}
}
public class AnimalFactory {
public static Animal createAnimalObject(String animalType, Object... args) {
try {
// loading the class by the classname here
Class<?> clazz = Class.forName(animalType);
// collecting the type of arguments that are part of the respective class constructor
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
// This will try to find the constructor of the provided class 'animalType' having constructor arguments in order as provided in 'args'
Constructor<?> constructor = clazz.getConstructor(argTypes);
// Creating a new Object using the constructor found above
return (Animal) constructor.newInstance(args);
}
catch (ClassNotFoundException |
NoSuchMethodException. |
InstantiationException |
IllegalAccessException |
InvocationTargetException e) {
throw new IllegalArgumentException("Invalid animal type or arguments", e);
}
}
}
// Usage below:
// This will pick the constructor created by us in Dog class to create the Dog object.
// This will not use the default constructor provided by Java because here we are passing args as "Ghost" in createAnimal method , which is the nickname of our Dog!
Animal animal = AnimalFactory.createAnimal("Dog", "Ghost");
animal.speak(); // Output: Woof woof! My nickname is: Ghost
// This will pick the default constructor of Java as we are not passing any args in createAnimal method
animal = AnimalFactory.createAnimal("Cat");
animal.speak(); // Output: Meow meow
In this example, the AnimalFactory
class has been modified to accept a variable number of arguments (Object... args
). The createAnimalObject()
method uses reflection to get the constructor of the object with the appropriate argument types and then invokes it with the provided arguments. This allows you to pass different arguments to the constructor of the object depending on the animalType
. Now we do not need to modify the createAnimalObject()
method of AnimalFactory
class and thus it no longer violates the OCP.
Lastly, if some of you are wondering that — in the future if we need to add more behaviors to the Animal
type (let’s say — eat(), drink()) then will it violate the OCP? The answer is — NO ❎.
In this case, the Animal
interface will be extended with two new behaviors: drink()
and eat()
. The concrete classes that implement the Animal
interface (Dog
and Cat
) will implement these new behaviors. This is an example of respecting the OCP because the existing code/behavior/logic of speak()
is not modified, but rather new functionalities of drink()
and eat()
have been added to it 😎. The code for the same is below:
public interface Animal {
// Existing behaviour
void speak();
// New behaviours
void drink();
void eat();
}
public class Dog implements Animal {
private String nickname;
public Dog(String nickname) {
this.nickname = nickname;
}
// No change to existing implementation
@Override
public void speak() {
System.out.println("Woof woof! My nickname is: " + this.nickname);
}
// New implementation, hence extending the existing functionality
@Override
public void drink() {
System.out.println("Woof woof! I am drinking!");
}
// New implementation, hence extending the existing functionality
@Override
public void eat() {
System.out.println("Woof woof! I am eating!");
}
}
public class Cat implements Animal {
// No change to existing implementation
@Override
public void speak() {
System.out.println("Meow meow");
}
// New implementation, hence extending the existing functionality
@Override
public void drink() {
System.out.println("Meow meow! I am drinking!");
}
// New implementation, hence extending the existing functionality
@Override
public void eat() {
System.out.println("Meow meow! I am eating!");
}
}
Now we are really done with the Simple factory pattern! Yay!! 🎊 🎊
[Variation 2] Factory method pattern
Now, let’s start with our second variation of the factory design pattern.
In this variation of the factory design pattern:
- We define a factory method in an interface or abstract class, and then
- We implement the factory method in the concrete classes.
Not understood? Don’t worry. You will understand this once you read the below code:
First of all, let’s bring our old interface and classes back:
public interface Animal {
void speak();
}
public class Dog implements Animal {
@Override
public void speak() {
System.out.println("Woof woof");
}
}
public class Cat implements Animal {
@Override
public void speak() {
System.out.println("Meow meow");
}
}
Now, all we need is to create a “factory” for the creation of objects we are interested in. See the below code on how we do that for this variation of the factory design pattern:
[Step a] First we create an interface for the factory. This interface has a “factory method” (here createAnimal()
) defined in it. This factory method will be used to create the required objects later on in the implementations of this AnimalFactory
interface:
public interface AnimalFactory {
Animal createAnimal();
}
[Step b] Create the factory classes for each class by implementing the above interface. For our example, we will be needing to create two factory classes in total i.e. one each for classes -Cat
and Dog
:
// Factory that will supply us with Dog objects
public class DogFactory implements AnimalFactory {
@Override
public Animal createAnimal() { // <----- This is the factory method
return new Dog();
}
}
// Factory that will supply us with Cat objects
public class CatFactory implements AnimalFactory {
@Override
public Animal createAnimal() { // <----- This is the factory method
return new Cat();
}
}
Okay, so now we have created the factory classes as above. Let’s see how the clients will use these factory classes to create required objects:
// Usage
AnimalFactory factory = new DogFactory(); // This will return Object of DogFactory
Animal animal = factory.createAnimal(); // This will return the Dog object
animal.speak(); // Output: Woof woof
factory = new CatFactory(); // This will return Object of CatFactory
animal = factory.createAnimal(); // This will return the Cat object
animal.speak(); // Output: Meow meow
So, what we have essentially achieved by doing this?
- It allows us to create required objects without specifying the exact class of object that will be created. This can be useful when we don’t know beforehand what specific type of object it needs to create.
- It can make it easier to test and maintain the code since the object creation logic is isolated in the factory method and can be tested independently from the rest of the code.
- We can introduce new types of Animal into the program without breaking/modifying the existing code. Thus, it follows OCP.
That’s it! we are done with the Factory method pattern! Yay!! 🎊 🎉
[Variation 3] Abstract factory pattern
Now, let’s discuss the third and last variation of the factory design pattern — the abstract factory pattern.
- Earlier, the factory method pattern was used to create a single object. But, using the abstract factory pattern we can create a family of related objects.
Are you thinking what does that even mean? The family of related objects means a group of objects that can work together or are compatible. Well, it will get more clear when I explain this with the code. For now, let’s hold on to the fact that it is used to create a family of related objects. Okay? Just trust me and read on… 😁
First of all, let’s bring back our old interface and classes — Animal
, Cat
and Dog
.
public interface Animal {
void speak();
}
public class Dog implements Animal {
@Override
public void speak() {
System.out.println("Woof woof");
}
}
public class Cat implements Animal {
@Override
public void speak() {
System.out.println("Meow meow");
}
}
Now, let’s say we want to give our Cat
and Dog
some tasty food. Let’s not keep them hungry! 🤤
public interface Food {
public void printType();
}
public class DogFood implements Food {
public void printType() {
System.out.println("Dog food");
}
}
public class CatFood implements Food {
public void printType() {
System.out.println("Cat food");
}
}
The above code is self-explanatory, right 😅?
Now, can you see that the classes Dog
and DogFood
are related? I mean you can give DogFood to Dog for eating (considering the ideal scenario 😜).
Similarly, the classes Cat
and CatFood
are related.
Hence, I have just established a relation between the objects of these classes. I want these two families of related objects to be created —
Family 1 — [Dog
, DogFood
]
Family 2 — [Cat
, CatFood
]
The abstract factory pattern is going to help us do just that — i.e. — creating a family of related objects.
Let’s see the code to understand how it will exactly help us to do that:
[Step a] Let’s create the abstract factory interface first. The implementations of this factory interface will help us create related objects. The implementations of this interface will ensure that the Animal
type and Food
type objects that it is creating are related to each other. For example — the implementation which is creating a Dog
object should ensure that only DogFood
object is created. The implementation class should never create unrelated objects, for eg. — Dog
and CatFood
interface AnimalFoodFactory {
public Animal createAnimal();
public Food createFood();
}
[Step b] Now, let’s create the first implementation of AnimalFoodFactory
class which will help us create the related objects — Dog
and DogFood
class DogFoodFactory implements AnimalFoodFactory {
public Animal createAnimal() {
return new Dog();
}
public Food createFood() {
return new DogFood();
}
}
[Step c] Similar to the above step, let’s create the second implementation of AnimalFoodFactory
class which will help us create the related objects —Cat
and CatFood
class CatFoodFactory implements AnimalFoodFactory {
public Animal createAnimal() {
return new Cat();
}
public Food createFood() {
return new CatFood();
}
}
That’s it! Let’s see how the client is going to use it:
// Using the implementation of abstract factory.
// Below code line will help us get the object of DogFoodFactory which will inturn help us create the related objects - Dog and DogFood
AnimalFoodFactory animalAndFoodFactory = new DogFoodFactory();
Animal animal = animalAndFoodFactory.createAnimal(); // this will return us the Dog object
animal.speak(); // Output: "Woof woof"
Food food = animalAndFoodFactory.createFood(); // this will return us the DogFood object
food.printType(); // Output: "Dog food"
Similarly, if we want to create related objects — Cat
and CatFood
:
// Using the implementation of abstract factory.
// Below code line will help us get the object of CatFoodFactory which will inturn help us create the related objects - Cat and CatFood
AnimalFoodFactory animalAndFoodFactory = new CatFoodFactory();
Animal animal = animalAndFoodFactory.createAnimal(); // this will return us the Cat object
animal.speak(); // Output: "Meow meow"
Food food = animalAndFoodFactory.createFood(); // this will return us the CatFood object
food.printType(); // Output: "Cat food"
Thus, we as a client were able to create a family of related objects without actually worrying about which actual classes are related and need to be instantiated. All of the instantiation logic were decoupled with the help of abstract factory implementation classes.
Thus it allowed the client application to be flexible and extensible, as it can easily support the addition of new types of Animal
and their corresponding Food
without requiring any changes to the existing code that uses the abstract factory. Thus, it also supports OCP.
That’s it! We have successfully covered all three variations of the factory design pattern. Yay!! 🎊 🎉🎊 🎉🎊 🎉
Please let me know in the comments if you have any suggestions/queries/appreciation 😁.
Take care guys! See you in my other articles.