Abstract Factory Pattern: Factories Making Factories
So far, we’ve looked at Singleton, Factory, Strategy, and Decorator patterns—each solving specific design problems. Today, we’re zooming in on a creational pattern that helps you create related or dependent objects without specifying their concrete classes: the Abstract Factory Pattern.
If you’re working on a backend application that needs to support multiple environments, vendors, or configurations, this pattern could be a lifesaver.
What Is the Abstract Factory Pattern?
The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. You define an abstract factory with methods for creating each kind of object in the family. Then, concrete factories implement those methods to return products that belong together.
It's like having a "theme pack" or "brand pack" that generates consistent objects.
Real-World Analogy: Furniture Set Factory
Imagine you run a furniture store and want to sell themed furniture sets. You have two themes: Modern and Victorian. Each set includes a chair and a table—but you don’t want customers mixing a modern chair with a Victorian table.
With the Abstract Factory Pattern, each "factory" produces a full matching set of furniture—ensuring consistency across product types without manually checking combinations.
Real Backend Example: Multi-Database Support in Python
Let’s say you’re building a backend app that supports both PostgreSQL and MongoDB. Each database has its own way of connecting, querying, and logging queries. You want to abstract away those differences and make it easy to switch backends.
Step 1: Define Abstract Product Interfaces
from abc import ABC, abstractmethod class Connection(ABC): @abstractmethod def connect(self): pass class QueryLogger(ABC): @abstractmethod def log(self, query: str): pass
Step 2: Create Abstract Factory Interface
class DatabaseFactory(ABC): @abstractmethod def create_connection(self) -> Connection: pass @abstractmethod def create_logger(self) -> QueryLogger: pass
Step 3: Implement Concrete Products for PostgreSQL
class PostgresConnection(Connection): def connect(self): return "Connected to PostgreSQL" class PostgresLogger(QueryLogger): def log(self, query: str): print(f"[Postgres] Executing query: {query}")
Step 4: Implement Concrete Products for MongoDB
class MongoConnection(Connection): def connect(self): return "Connected to MongoDB" class MongoLogger(QueryLogger): def log(self, query: str): print(f"[MongoDB] Executing query: {query}")
Step 5: Implement Concrete Factories
class PostgresFactory(DatabaseFactory): def create_connection(self) -> Connection: return PostgresConnection() def create_logger(self) -> QueryLogger: return PostgresLogger() class MongoFactory(DatabaseFactory): def create_connection(self) -> Connection: return MongoConnection() def create_logger(self) -> QueryLogger: return MongoLogger()
Usage Example
def use_database(factory: DatabaseFactory): connection = factory.create_connection() logger = factory.create_logger() print(connection.connect()) logger.log("SELECT * FROM users") # Switch backend here db_factory = PostgresFactory() use_database(db_factory) # Or Mongo db_factory = MongoFactory() use_database(db_factory)
Now you can switch the entire backend database behavior by changing just one factory. This is especially useful in multi-tenant, multi-environment, or plugin-based systems.
Where Abstract Factory Shines in Backend Systems
- Supporting multiple databases (SQL vs NoSQL)
- Building plugin systems where each plugin provides its own behavior
- Multi-branding support (e.g., white-label apps with brand-specific UI/backend behavior)
- Choosing between API versions or providers (e.g., Stripe vs PayPal)
- Environment-based service creation (e.g., local, staging, production)
Gotchas
- Complexity: Abstract Factory can add a lot of classes quickly, especially when you have many product types.
- Rigidity: All products must follow the same abstract interface structure, which can be limiting in edge cases.
- Verbosity: This pattern works best when there's a clear and consistent product family.
Final Thoughts
The Abstract Factory Pattern brings structure and consistency to object creation when you have multiple variants of related objects. It’s particularly useful in complex backend systems where flexibility and configurability are key.
By encapsulating all variations in a factory, your core system logic becomes cleaner, more modular, and easier to extend.