Factory Method Pattern — Object Creation Interface
What You'll Learn
You will learn how the Factory Method pattern delegates object instantiation to subclasses, enabling your code to work with families of related objects without coupling to concrete classes. You will also understand when to apply it versus other creational patterns.
Why It Matters
Direct instantiation with new binds your code to specific implementations. When requirements change, every call site must be updated. Factory Method encapsulates creation logic, making your system open for extension but closed for modification — a cornerstone of the Open/Closed Principle. Imagine your application exports reports in multiple formats. Without Factory Method, adding a new format like CSV means hunting down every place where exporters are instantiated and adding a conditional branch. With Factory Method, you simply add a new concrete Factory class and the existing dispatch code works unchanged.
Real-World Use
GUI frameworks use Factory Methods to create platform-specific buttons and dialogs without altering the core application logic. DodaTech's plugin system uses Factory methods to instantiate the correct data-source handler based on configuration files. When a user configures a PostgreSQL connection, the Factory returns a PostgresHandler; for a MySQL connection, it returns a MySQLHandler — and neither the configuration parser nor the query executor knows which concrete handler was chosen.
The Pattern
A creator class declares the Factory Method and may provide a default implementation. Concrete creators override it to return specific products.
from abc import ABC, abstractmethod
class Exporter(ABC):
@abstractmethod
def export(self, data: str) -> str:
pass
class JsonExporter(Exporter):
def export(self, data: str) -> str:
return f'{{"data": "{data}"}}'
class XmlExporter(Exporter):
def export(self, data: str) -> str:
return f"<data>{data}</data>"
class ExporterFactory(ABC):
@abstractmethod
def create_exporter(self) -> Exporter:
pass
class JsonExporterFactory(ExporterFactory):
def create_exporter(self) -> Exporter:
return JsonExporter()
class XmlExporterFactory(ExporterFactory):
def create_exporter(self) -> Exporter:
return XmlExporter()
def run_export(factory: ExporterFactory, data: str):
exporter = factory.create_exporter()
print(exporter.export(data))
run_export(JsonExporterFactory(), "hello")
run_export(XmlExporterFactory(), "world")
{"data": "hello"}
<data>world</data>
Structure
classDiagram
class Creator {
+factoryMethod(): Product
+operation()
}
class ConcreteCreatorA {
+factoryMethod(): Product
}
class ConcreteCreatorB {
+factoryMethod(): Product
}
class Product {
<>
}
class ConcreteProductA
class ConcreteProductB
Creator --> Product : creates
ConcreteCreatorA --> ConcreteProductA : creates
ConcreteCreatorB --> ConcreteProductB : creates
ConcreteCreatorA --|> Creator
ConcreteCreatorB --|> Creator
ConcreteProductA ..|> Product
ConcreteProductB ..|> Product
Real-World Usage
- Django REST Framework serializers —
SerializerClassattribute acts as a Factory Method for different serialisation strategies. - Java
Collection.<a href="/design-patterns/iterator/">Iterator</a>()— each collection type returns its own Iterator via a Factory Method. - .NET
WebRequest.Create()— creates protocol-specific request objects (HTTP, FTP) from a URL string.
Related Patterns
- Abstract Factory is often implemented as a collection of factory methods.
- Template Method frequently calls Factory methods inside its algorithm skeleton.
- Prototype offers an alternative creation approach through cloning.
Pros and Cons
| Pros | Cons |
|---|---|
| Eliminates tight coupling to concrete classes | Can lead to many small Factory classes |
| Follows the Open/Closed Principle | Increases complexity when creation logic is simple |
| Supports parallel class hierarchies | Clients may need to subclass just to create objects |
| Promotes consistent object creation across subsystems | Overuse can make code harder to follow |
The Factory Method pattern buys you flexibility through polymorphism. The run_export function accepts any ExporterFactory — adding a new export format means creating a new Factory class without changing run_export at all. This is the essence of the Open/Closed Principle in action. The pattern also supports dependency inversion: high-level code depends on the abstract ExporterFactory, not on concrete JsonExporterFactory or XmlExporterFactory.
Practice Questions
- How does Factory Method differ from using a simple static Factory or utility class? Consider the role of inheritance and polymorphism.
- What happens to the pattern when the product hierarchy spans more than one dimension? For example, what if exporters vary by format and by destination?
- When would you choose Factory Method over passing a class reference directly? Think about configuration-driven applications.
- Implement a Factory Method that caches created instances, returning a previously created product for repeated calls with the same parameters.
Challenge
Extend the exporter example above to support a third format — CSV — without modifying any existing Factory or product classes. Verify that run_export works with the new Factory unchanged.
Real-World Task
In your current project, identify a switch statement or if-else chain that selects a concrete class based on a string or enum. Refactor it into a Factory Method hierarchy and use DodaTech's pattern sandbox to validate the new design before merging.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro