1. The Problem
Problem: Object Creation Logic Is Scattered, Hard-coded, and Brittle
Systems often need to create objects belonging to a family of related types.
Example: creating different parsers (JSONParser, XMLParser, YAMLParser) depending on input.
Naive code:
if type == "json": return JSONParser()
elif type == "xml": return XMLParser()
elif type == "yaml": return YAMLParser()
Issues:
- Object creation logic spreads across the codebase.
- Violates Open/Closed Principle (every new type requires modifying callers).
- Causes branching explosion.
- Makes testing harder.
- Couples high-level code to concrete types.
We need a way to centralize and abstract creation, keeping client code unaware of concrete implementations.
2. Unified Concept: The Factory Pattern
Factory = decouple what you create from how you create it.
There are two levels of abstraction:
2.1 Simple Factory (not a GoF pattern, but widely used)
- One class/function that selects and instantiates concrete types.
- Encapsulates the branching logic.
- Caller asks for a product by name/config, gets the right instance.
Use it when:
- You want to centralize creation.
- You want to hide type-selection logic.
- You don’t need subclassing for creation.
2.2 Factory Method (GoF pattern)
- Creation is delegated to subclasses.
- A base class defines a generic operation.
- Subclasses override the factory method to specify concrete types.
Use it when:
- You want object creation to vary with subclasses.
- Creation must be part of a larger algorithm (Template Method synergy).
- You need polymorphic creation.
Unification Mental Model:
- Simple Factory → procedural creation encapsulation.
- Factory Method → polymorphic creation encapsulation.
- Both solve the same problem: caller shouldn’t know concrete classes.
- The difference is where the variation lives:
- Simple Factory: inside one place.
- Factory Method: across subclasses.
3. Implementation: Simple Factory + Factory Method in One Flow
3.1 Product interface
from abc import ABC, abstractmethod
class Parser(ABC):
@abstractmethod
def parse(self, data: str) -> dict:
pass
3.2 Concrete products
class JSONParser(Parser):
def parse(self, data: str) -> dict:
return {"json": data}
class XMLParser(Parser):
def parse(self, data: str) -> dict:
return {"xml": data}
3A. Simple Factory Implementation
A single class that decides which product to create.
class ParserFactory:
@staticmethod
def create_parser(type_: str) -> Parser:
if type_ == "json":
return JSONParser()
if type_ == "xml":
return XMLParser()
raise ValueError("Unknown parser type")
Usage:
parser = ParserFactory.create_parser("json")
Advantages:
- Centralized creation.
- Caller deals with abstraction only.
Limitations:
- Adding a new parser modifies the factory.
- Still breaks Open/Closed.
3B. Factory Method Implementation (GoF)
Context class holds a template method that relies on a factory method.
class ParserApp(ABC):
@abstractmethod
def create_parser(self) -> Parser:
pass
def run(self, data: str):
parser = self.create_parser()
return parser.parse(data)
Concrete creators:
class JSONParserApp(ParserApp):
def create_parser(self) -> Parser:
return JSONParser()
class XMLParserApp(ParserApp):
def create_parser(self) -> Parser:
return XMLParser()
Usage:
app = JSONParserApp()
result = app.run("data")
Advantages:
- Adding new parser = adding new subclass.
- No modification to existing code.
- Creation integrated into algorithmic flow.
4. When to Use Simple Factory vs Factory Method
Use Simple Factory when:
- You only need one place to manage object creation.
- You want to hide branching, not eliminate it.
- You don’t need inheritance-based extensibility.
Use Factory Method when:
- Creation varies by subclass.
- You want Open/Closed compliant extensibility.
- Creation is part of a broader operation.
- You want polymorphic creation.
Mental shortcut:
- Simple Factory → procedural switch.
- Factory Method → polymorphic override.
5. Common Pitfalls
- Overusing factories for trivial objects → unnecessary complexity.
- Turning every variation into a subclass → class explosion.
- Letting factories leak concrete types → breaks encapsulation.
- Mixing product construction with configuration logic → unclear boundaries.
- Using factories to hide global state → anti-pattern.