Factory Pattern: Creational Design Pattern
Factory Pattern is one of the most commonly used creational design patterns in software development, particularly in Java and C++. It provides a standardized way to create objects while hiding the underlying instantiation logic from client code, and uses a shared interface to reference the newly created objects.
Core Intent
Define an interface for creating an object, but defer the actual instantiation to subclasses, allowing each subclass to determine which concrete object type to create.
Problem Solved
This pattern addresses the challenge of selecting appropriate concrete object implementations without coupling client code to specific product classes. It centralizes object creation logic to reduce code duplication and improve maintainability.
When to Use
Use this pattern when you need to create different object instances based on runtime conditions, or when you do not know the exact concrete classes of objects you need to create ahead of time.
Key Implementation Details
Object creation logic is executed in factory subclasses, with all products implementing a common abstract interface. Clients interact only with this interface and factory methods, never directly with concrete product classes.
Common Use Cases
- Vehicle manufacturing: Ordering a vehicle without needing to understand its assembly process
- Database abstraction layers: Swapping database backends by adjusting dialect and driver configurations, as seen in Hibernate
- Logging frameworks: Supporting multiple logging targets such as local files, system event logs, or remote servers
- Protocol handling: Implementing common interfaces for network protocols like POP3, IMAP, and HTTP
Pros and Cons
Advantages
- Clients only need to know the identifier for the desired object type and its shared interface
- High extensibility: Adding new product types only requires creating a new concrete product class and updating factory logic
- Decouples object usage from object creation, adhering to the single responsibility principle
- Centralizes object creation code to avoid duplication across the codebase
Disadvantages
- Each new product addition requires creating a new concrete class and modifying factory logic, leading to an increase in total class count and system complexity
- Overuse for simple objects that can be directly instantiated adds unnecessary overhead
Implementation Examples
C++ Implementation
Step 1: Define Abstract Shape Interface
Create a base abstract class for all shape products.
shape_base.h
#pragma once
#include <iostream>
class Shape {
public:
virtual ~Shape() = default;
virtual void render() const = 0;
};
Step 2: Create Concrete Shape Classes
Implement the abstract interface for specific shape types.
circle_shape.h
#pragma once
#include "shape_base.h"
class CircleShape : public Shape {
public:
void render() const override {
std::cout << "Drawing a circle\n";
}
};
rect_shape.h
#pragma once
#include "shape_base.h"
class RectShape : public Shape {
public:
void render() const override {
std::cout << "Drawing a rectangle\n";
}
};
square_shape.h
#pragma once
#include "shape_base.h"
class SquareShape : public Shape {
public:
void render() const override {
std::cout << "Drawing a square\n";
}
};
Step 3: Build the Shape Factory Class
Create a factory class that generates concrete shape objects based on input type.
shape_factory.h
#pragma once
#include "shape_base.h"
#include "circle_shape.h"
#include "rect_shape.h"
#include "square_shape.h"
#include <string>
class ShapeFactoryClass {
public:
Shape* createShape(const std::string& shapeType) {
if (shapeType.empty()) {
return nullptr;
}
if (shapeType == "circle") {
return new CircleShape();
} else if (shapeType == "rectangle") {
return new RectShape();
} else if (shapeType == "square") {
return new SquareShape();
}
return nullptr;
}
};
Step 4: Implement Client Code
Use the factory to retrieve and interact with shape objects.
main.cpp
#include <iostream>
#include "shape_factory.h"
#include <memory>
int main() {
std::unique_ptr<ShapeFactoryClass> factory = std::make_unique<ShapeFactoryClass>();
std::unique_ptr<Shape> circle = std::unique_ptr<Shape>(factory->createShape("circle"));
circle->render();
std::unique_ptr<Shape> rect = std::unique_ptr<Shape>(factory->createShape("rectangle"));
rect->render();
std::unique_ptr<Shape> square = std::unique_ptr<Shape>(factory->createShape("square"));
square->render();
std::cin.get();
return 0;
}
Sample Output
Drawing a circle
Drawing a rectangle
Drawing a square
Java Implementation
Step 1: Define the Shape Interface
Shape.java
public interface Shape {
void draw();
}
Step 2: Create Concrete Shape Classes
Implement the shared interface for each shape type.
CircleShape.java
public class CircleShape implements Shape {
@Override
public void draw() {
System.out.println("Rendering circle shape");
}
}
RectangleShape.java
public class RectangleShape implements Shape {
@Override
public void draw() {
System.out.println("Rendering rectangle shape");
}
}
SquareShape.java
public class SquareShape implements Shape {
@Override
public void draw() {
System.out.println("Rendering square shape");
}
}
Step 3: Build the Shape Factory
ShapeFactory.java
public class ShapeFactory {
public Shape getShapeInstance(String shapeType) {
if (shapeType == null) {
return null;
}
String normalizedType = shapeType.toLowerCase();
switch (normalizedType) {
case "circle":
return new CircleShape();
case "rectangle":
return new RectangleShape();
case "square":
return new SquareShape();
default:
return null;
}
}
}
Step 4: Create Demo Client Class
ShapeFactoryDemo.java
public class ShapeFactoryDemo {
public static void main(String[] args) {
ShapeFactory factory = new ShapeFactory();
Shape circle = factory.getShapeInstance("CIRCLE");
circle.draw();
Shape rectangle = factory.getShapeInstance("RECTANGLE");
rectangle.draw();
Shape square = factory.getShapeInstance("SQUARE");
square.draw();
}
}
Sample Output
Rendering circle shape
Rendering rectangle shape
Rendering square shape
Core Characteristics
- Classified as a creational design pattern
- Hides object creation logic from client code
- Uses a shared interface to standardize references to created objects