Table of Contents
Factory in Real World
A factory in the real world is a facility where items or products are manufactured. ex: A car factory is where cars are manufactured. If you want a car, do you want to manufacture or get one from a factory/plant?
Factory or simple Factory design pattern is part of GOF (Gang of Four) Object Oriented Design Patterns. It comes under Creational Design Patterns.
Creational Design Patterns guide developers on how to create the objects in your code that can help the application maintainability and testability in the long run.
Now let's jump in for the complete details of Factory Design Pattern.
Purpose
The purpose of Factory design pattern is to Encapsulate object creation in a separate class.
What does this mean? Let's understand.
for ex, If you want to create an object in Java, we create it using a new operator. This works well if the class for which you are creating the object is simple to understand.
There can be cases where a class can have different constructors and can expect different constructor parameters for object creation. In this case, as a developer you need to understand constructors well before object creation.
Also if you want to create the object in multiple places, you need to use the same code or steps for object creation. This causes code duplication in multiple places and later if you want to change the object creation, you will need to update in all places. This is the classical problem of code duplication.
Instead of you creating an object by yourself, If you delegate it to a separate class, then this problem is resolved. The other advantage is any new developer does not need to know the creation logic and can use the class whenever required.
When should you consider using Factory Pattern?
When you want to create instances of different types of objects that implement a common interface.
for example,
You have a UI application for creating shapes such as Circle, Square, Rectangle, Triangle, etc. To do this, On your canvas Object, you can instantiate all these objects and refer to the correct one based on the user's choice.
You need to create each object using a new operator in Java. It's easier in this case, but in a much more complex application, the process of object creation may require complex initialization steps, constructor parameters setting, or dependency dealing.
Creating an object every time using a new operator where ever can lead to code duplication, and code complexity, which eventually makes the application harder to maintain.
Solution:
Instead of creating objects whenever required, this can be delegated to the Factory class.
The factory class exposes different methods to create instances of the Shape class. It encapsulates the object creation in a single class. The clients (Canvas class in our example), can just ask the factory for new instances without worrying about how these instances are created.
This highlights the advantages of code being more manageable, testable, and easier to modify or extend.
Manageable - new factories can be created easily and can replace existing factories with fewer changes to clients.
Testable - Factory class & object creation can be tested independently from clients. Clients can mock the factory during the Unit testing.
Extensibility - new shapes can be added to factory class easily, without disrupting clients.
Algorithm or Steps to follow for creating Factory Pattern:
After reading many design patterns, I felt, they are easier to understand but difficult to implement. The reason is, that we don't know the steps to follow.
Follow the below steps for efficient implementation of the Factory Design Pattern. The same steps are implemented in the example given next.
The steps are devised based on our Shape factory example.
Step 1: Create a common interface or abstract class
We have different shapes such as Circle, Rectangle, Square. The first step is to create the Shape interface. You can also go with the Abstract class if you have some common functionality among all shapes.
Step 2: Create concrete classes by implementing/extending interface/abstract class
The next step is to create concrete classes such as Circle, Rectangle, etc. by implementing the interface created in Step 1.
Step 3: Create the factory class and provide methods to create the object
This is the step where your actual factory class implementation goes. You need to create the Factory class and provide the method or methods for instantiating objects for concrete classes.
Step 4: Create the clients and use the factory
In the final step, the clients no longer create the objects on their own but use the Factory class for the same purpose.
Following these steps brings the advantages of code being more manageable, testable, and easier to modify or extend.
Example:
// Step 1: Create common interface or abstract class
public interface Shape {
}
// Step 2: Create concreate classes by implementing/extending interace/abstract class
public class Circle implementes Shape {
}
public class Square implements Shape {
}
// Step 3: Create the factory class and provide methods to create the object
public class ShapeFactory {
private static final String CIRCLE = "circle";
private static final String SQUARE = "square";
public Shape createShape(String shape) {
if(CIRCLE.equals(shape)) {
return new Circle();
}
if(SQUARE.equals(shape)) {
return new Square();
}
return null;
}
}
// Step 4: Create the clients and use the factory
public class Canvas {
private ShapeFactory factory;
public Canvas(ShapeFactory factory) {
this.factory = factory;
}
public void drawShape(String shapeType) {
Shape shape = factory.createShape(shapeType);
// draw the shape, etc.
}
}
Advantages:
- Delegates object creation to a different class, there by removes code duplication.
- Easier to maintain or extend.
- Loose coupling.
- Since all object creation is in one place, Factory pattern makes it easier to replace these instances with stubs or mock objects for testing.
- It's easier to add new types of objects without disturbing existing code.
- Factories can also be implemented as Singleton to keep track of the object instances.
Disadvantages:
- Increases the code complextiy in Factory class when the application has large number of classes to manage, as all the object creation is routed through one factory class.
- Imagine having hundreds of shapes and creating all shape objects using one Shape Factory class makes shapes creation logic too complex.