Home/Design Patterns/Factory Method Pattern

Factory Method Pattern

Explain Like I'm 5...

Imagine you have a toy factory! When someone asks for a toy, the factory decides which toy to make - maybe a car, a doll, or a robot.

The Toy Factory:

  • You tell the factory: 'I want a vehicle toy!'
  • The factory boss decides: 'Okay, I'll make a car!' or 'I'll make a bike!'
  • You don't need to know HOW to make the toy - the factory does it for you!
  • Different factories can make different types of toys, but they all work the same way!

Why Is This Useful?

  • You don't need to know the secret recipe for making each toy!
  • The factory can decide which toy to make based on what you need!
  • If you want to add new toys, you just teach the factory - you don't change your request!

What is Factory Method Pattern?

The Factory Method pattern 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. Instead of calling a constructor directly, you call a factory method that returns the object.

Purpose

  • Define an interface for creating an object, but let subclasses decide which class to instantiate
  • Let a class defer instantiation to subclasses
  • Provide flexibility in deciding which objects to create

Key Components

1. Product: Product: The interface/abstract class for objects the factory method creates

2. ConcreteProduct: ConcreteProduct: Specific implementations of the Product interface

3. Creator: Creator: Declares the factory method that returns Product objects

4. ConcreteCreator: ConcreteCreator: Overrides factory method to return ConcreteProduct instances

Implementation 1: Shape Factory

A classic example where different shape factories create different shape objects.

Shape.java
java
1
2
3
4
5
// Product interface - defines what all shapes must do
public interface Shape {
void draw();
double calculateArea();
}
Circle.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ConcreteProduct - specific implementation of Shape
public class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a Circle with radius: " + radius);
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
Rectangle.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ConcreteProduct - another implementation of Shape
public class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing a Rectangle: " + width + "x" + height);
}
@Override
public double calculateArea() {
return width * height;
}
}
Square.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ConcreteProduct - square is a special rectangle
public class Square implements Shape {
private double side;
public Square(double side) {
this.side = side;
}
@Override
public void draw() {
System.out.println("Drawing a Square with side: " + side);
}
@Override
public double calculateArea() {
return side * side;
}
}
ShapeFactory.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Creator - abstract factory class
public abstract class ShapeFactory {
// Factory method - subclasses will override this
public abstract Shape createShape();
// Template method that uses the factory method
public void renderShape() {
// Use the factory method to get the shape
Shape shape = createShape();
// Common operations for all shapes
System.out.println("--- Rendering Shape ---");
shape.draw();
System.out.println("Area: " + shape.calculateArea());
System.out.println("----------------------");
}
}
CircleFactory.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ConcreteCreator - creates Circle objects
public class CircleFactory extends ShapeFactory {
private double radius;
public CircleFactory(double radius) {
this.radius = radius;
}
@Override
public Shape createShape() {
// This factory always creates Circle objects
return new Circle(radius);
}
}
RectangleFactory.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ConcreteCreator - creates Rectangle objects
public class RectangleFactory extends ShapeFactory {
private double width;
private double height;
public RectangleFactory(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public Shape createShape() {
// This factory always creates Rectangle objects
return new Rectangle(width, height);
}
}
ShapeFactoryDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Client code - uses factories to create shapes
public class ShapeFactoryDemo {
public static void main(String[] args) {
// Create different factories
ShapeFactory circleFactory = new CircleFactory(5.0);
ShapeFactory rectangleFactory = new RectangleFactory(4.0, 6.0);
// Use factories to create and render shapes
// The client doesn't need to know which concrete class is created
System.out.println("Creating and rendering shapes:");
circleFactory.renderShape();
rectangleFactory.renderShape();
// We can easily add more shape types without changing client code
ShapeFactory squareFactory = new ShapeFactory() {
@Override
public Shape createShape() {
return new Square(7.0);
}
};
squareFactory.renderShape();
}
}
/* Output:
Creating and rendering shapes:
--- Rendering Shape ---
Drawing a Circle with radius: 5.0
Area: 78.53981633974483
----------------------
--- Rendering Shape ---
Drawing a Rectangle: 4.0x6.0
Area: 24.0
----------------------
--- Rendering Shape ---
Drawing a Square with side: 7.0
Area: 49.0
----------------------
*/

Implementation 2: Document Factory

Creating different types of documents (PDF, Word, Excel) using factory method.

Document.java
java
1
2
3
4
5
6
7
// Product interface - all documents must implement these methods
public interface Document {
void open();
void save();
void close();
String getType();
}
PDFDocument.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ConcreteProduct - PDF implementation
public class PDFDocument implements Document {
private String fileName;
public PDFDocument(String fileName) {
this.fileName = fileName;
}
@Override
public void open() {
System.out.println("Opening PDF document: " + fileName);
System.out.println("Loading PDF renderer...");
}
@Override
public void save() {
System.out.println("Saving PDF document with compression...");
}
@Override
public void close() {
System.out.println("Closing PDF document: " + fileName);
}
@Override
public String getType() {
return "PDF";
}
}
WordDocument.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ConcreteProduct - Word document implementation
public class WordDocument implements Document {
private String fileName;
public WordDocument(String fileName) {
this.fileName = fileName;
}
@Override
public void open() {
System.out.println("Opening Word document: " + fileName);
System.out.println("Loading MS Word engine...");
}
@Override
public void save() {
System.out.println("Saving Word document in .docx format...");
}
@Override
public void close() {
System.out.println("Closing Word document: " + fileName);
}
@Override
public String getType() {
return "Word";
}
}
ExcelDocument.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ConcreteProduct - Excel spreadsheet implementation
public class ExcelDocument implements Document {
private String fileName;
public ExcelDocument(String fileName) {
this.fileName = fileName;
}
@Override
public void open() {
System.out.println("Opening Excel document: " + fileName);
System.out.println("Loading spreadsheet engine...");
}
@Override
public void save() {
System.out.println("Saving Excel document with formulas...");
}
@Override
public void close() {
System.out.println("Closing Excel document: " + fileName);
}
@Override
public String getType() {
return "Excel";
}
}
DocumentFactory.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Creator - abstract factory for documents
public abstract class DocumentFactory {
// Factory method - to be implemented by subclasses
public abstract Document createDocument(String fileName);
// Template method using the factory method
public void processDocument(String fileName) {
// Create document using factory method
Document doc = createDocument(fileName);
// Perform common operations
System.out.println("\n=== Processing " + doc.getType() + " Document ===");
doc.open();
// Simulate some work
System.out.println("... working on document ...");
doc.save();
doc.close();
System.out.println("=== Done ===\n");
}
}
ConcreteFactories.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ConcreteCreators - specific factory implementations
public class PDFDocumentFactory extends DocumentFactory {
@Override
public Document createDocument(String fileName) {
return new PDFDocument(fileName);
}
}
public class WordDocumentFactory extends DocumentFactory {
@Override
public Document createDocument(String fileName) {
return new WordDocument(fileName);
}
}
public class ExcelDocumentFactory extends DocumentFactory {
@Override
public Document createDocument(String fileName) {
return new ExcelDocument(fileName);
}
}
DocumentFactoryDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Client code demonstrating document creation
public class DocumentFactoryDemo {
public static void main(String[] args) {
// Create different document factories
DocumentFactory pdfFactory = new PDFDocumentFactory();
DocumentFactory wordFactory = new WordDocumentFactory();
DocumentFactory excelFactory = new ExcelDocumentFactory();
// Process different types of documents
// The client code doesn't need to know the concrete document classes
pdfFactory.processDocument("report.pdf");
wordFactory.processDocument("letter.docx");
excelFactory.processDocument("budget.xlsx");
// Example: Document processor that works with any factory
processMultipleDocuments(pdfFactory,
new String[]{"doc1.pdf", "doc2.pdf"});
}
// This method demonstrates polymorphism
// It works with any DocumentFactory subclass
private static void processMultipleDocuments(
DocumentFactory factory, String[] fileNames) {
System.out.println("Batch processing documents...");
for (String fileName : fileNames) {
factory.processDocument(fileName);
}
}
}
/* Output:
=== Processing PDF Document ===
Opening PDF document: report.pdf
Loading PDF renderer...
... working on document ...
Saving PDF document with compression...
Closing PDF document: report.pdf
=== Done ===
=== Processing Word Document ===
Opening Word document: letter.docx
Loading MS Word engine...
... working on document ...
Saving Word document in .docx format...
Closing Word document: letter.docx
=== Done ===
...
*/

Implementation 3: Transportation Factory

Creating different vehicles for transportation logistics.

Transport.java
java
1
2
3
4
5
6
// Product interface - common interface for all transport types
public interface Transport {
void deliver(String destination, int packages);
String getTransportType();
double calculateCost(int distance);
}
Truck.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ConcreteProduct - Truck implementation
public class Truck implements Transport {
private static final double COST_PER_KM = 2.5;
@Override
public void deliver(String destination, int packages) {
System.out.println("Delivering " + packages + " packages by TRUCK");
System.out.println("Destination: " + destination);
System.out.println("Loading packages into truck container...");
System.out.println("Driving on roads...");
}
@Override
public String getTransportType() {
return "Truck";
}
@Override
public double calculateCost(int distance) {
return distance * COST_PER_KM;
}
}
Ship.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ConcreteProduct - Ship implementation
public class Ship implements Transport {
private static final double COST_PER_KM = 1.5;
@Override
public void deliver(String destination, int packages) {
System.out.println("Delivering " + packages + " packages by SHIP");
System.out.println("Destination: " + destination);
System.out.println("Loading packages into containers...");
System.out.println("Sailing across the ocean...");
}
@Override
public String getTransportType() {
return "Ship";
}
@Override
public double calculateCost(int distance) {
// Ships are cheaper per km but have a base port fee
return (distance * COST_PER_KM) + 500; // 500 is port fee
}
}
Plane.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ConcreteProduct - Plane implementation
public class Plane implements Transport {
private static final double COST_PER_KM = 5.0;
@Override
public void deliver(String destination, int packages) {
System.out.println("Delivering " + packages + " packages by PLANE");
System.out.println("Destination: " + destination);
System.out.println("Loading packages into aircraft cargo...");
System.out.println("Flying through the sky...");
}
@Override
public String getTransportType() {
return "Plane";
}
@Override
public double calculateCost(int distance) {
// Planes are expensive but fast
return distance * COST_PER_KM;
}
}
Logistics.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Creator - abstract logistics company
public abstract class Logistics {
// Factory method - subclasses decide which transport to create
public abstract Transport createTransport();
// Template method for planning delivery
public void planDelivery(String destination, int packages, int distance) {
// Create appropriate transport using factory method
Transport transport = createTransport();
System.out.println("\n===== Delivery Planning =====");
System.out.println("Transport Type: " + transport.getTransportType());
System.out.println("Distance: " + distance + " km");
// Calculate cost
double cost = transport.calculateCost(distance);
System.out.println("Estimated Cost: $" + cost);
// Execute delivery
transport.deliver(destination, packages);
System.out.println("Delivery completed!");
System.out.println("============================\n");
}
}
ConcreteLogistics.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ConcreteCreators - specific logistics companies
// Road logistics - uses trucks
public class RoadLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Truck();
}
}
// Sea logistics - uses ships
public class SeaLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Ship();
}
}
// Air logistics - uses planes
public class AirLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Plane();
}
}
TransportationDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Client code demonstrating logistics system
public class TransportationDemo {
public static void main(String[] args) {
// Based on requirements, choose appropriate logistics
// Scenario 1: Domestic delivery (short distance)
System.out.println("Scenario 1: Domestic delivery");
Logistics domesticLogistics = new RoadLogistics();
domesticLogistics.planDelivery("New York", 50, 200);
// Scenario 2: International delivery (long distance, many packages)
System.out.println("Scenario 2: International shipping");
Logistics internationalLogistics = new SeaLogistics();
internationalLogistics.planDelivery("London", 1000, 5000);
// Scenario 3: Express delivery (urgent)
System.out.println("Scenario 3: Express delivery");
Logistics expressLogistics = new AirLogistics();
expressLogistics.planDelivery("Tokyo", 20, 10000);
// Demonstrate flexibility: runtime decision
System.out.println("\nDynamic decision based on distance:");
int distance = 300;
Logistics chosenLogistics = selectLogistics(distance);
chosenLogistics.planDelivery("Chicago", 75, distance);
}
// Factory method to select logistics based on business rules
private static Logistics selectLogistics(int distance) {
if (distance < 500) {
System.out.println("Selected: Road logistics (short distance)");
return new RoadLogistics();
} else if (distance < 3000) {
System.out.println("Selected: Air logistics (medium distance)");
return new AirLogistics();
} else {
System.out.println("Selected: Sea logistics (long distance)");
return new SeaLogistics();
}
}
}
/* Output:
Scenario 1: Domestic delivery
===== Delivery Planning =====
Transport Type: Truck
Distance: 200 km
Estimated Cost: $500.0
Delivering 50 packages by TRUCK
Destination: New York
Loading packages into truck container...
Driving on roads...
Delivery completed!
============================
...
*/

Real-World Examples

UI Component Frameworks

Frameworks like React or Android use factory methods to create different UI components based on configuration

Notification Systems

Email, SMS, or Push notification creators that all implement the same interface but create different notification types

Payment Gateways

PayPal, Stripe, or Credit Card payment processors where the factory creates the appropriate payment handler

Database Connections

JDBC drivers use factory methods to create connections for different database types (MySQL, PostgreSQL, etc.)

When to Use Factory Method

  • When you don't know beforehand the exact types and dependencies of objects your code should work with
  • When you want to provide a way to extend internal components
  • When you want to save system resources by reusing existing objects instead of rebuilding them
  • When you want to delegate the responsibility of creating objects to helper subclasses

UML Diagram Explanation

The Factory Method pattern structure:

  • Creator class declares the factory method that must return an object of a Product class
  • ConcreteCreator overrides the factory method to return different types of products
  • Product defines the interface that all products must implement
  • ConcreteProducts are different implementations of the Product interface

Benefits

  • +Loose Coupling: Code depends on interfaces, not concrete classes
  • +Single Responsibility Principle: Object creation code is in one place
  • +Open/Closed Principle: New product types without changing existing code
  • +Flexibility: Easy to add new product types by creating new factory subclasses
  • +Code Reusability: Common object creation logic in base factory class

Drawbacks

  • -Code Complexity: Pattern requires many new subclasses
  • -Overhead: May be overkill for simple object creation
  • -Learning Curve: Team members need to understand the pattern

Key Points to Remember

  • 1Factory Method is about polymorphism - using inheritance and overriding
  • 2Promotes loose coupling by eliminating the need to bind application classes to concrete classes
  • 3Follows Open/Closed Principle - open for extension, closed for modification
  • 4The factory method can have default implementation in the base class
  • 5Subclasses decide which concrete class to instantiate

Practice Scenarios

  • Create a Logger factory that creates FileLogger, ConsoleLogger, or DatabaseLogger
  • Implement a Vehicle Rental System with factories for different vehicle types
  • Build a Report Generator that creates PDF, Excel, or HTML reports
  • Design a Button factory for different operating systems (Windows, Mac, Linux)
  • Create a Database Connection factory supporting multiple database types