Abstract Factory Pattern
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Problem
You need to create groups of related objects (e.g., UI components for Windows vs Mac) that must be used together consistently.
Solution
The Abstract Factory pattern provides an interface for creating families of related objects.
Implementation
from abc import ABC, abstractmethod
# ============ ABSTRACT PRODUCTS ============
class Button(ABC):
@abstractmethod
def render(self):
pass
class Checkbox(ABC):
@abstractmethod
def render(self):
pass
# ============ CONCRETE PRODUCTS: Windows ============
class WindowsButton(Button):
def render(self):
return "Rendering Windows button"
class WindowsCheckbox(Checkbox):
def render(self):
return "Rendering Windows checkbox"
# ============ CONCRETE PRODUCTS: Mac ============
class MacButton(Button):
def render(self):
return "Rendering Mac button"
class MacCheckbox(Checkbox):
def render(self):
return "Rendering Mac checkbox"
# ============ CONCRETE PRODUCTS: Linux ============
class LinuxButton(Button):
def render(self):
return "Rendering Linux button"
class LinuxCheckbox(Checkbox):
def render(self):
return "Rendering Linux checkbox"
# ============ ABSTRACT FACTORY ============
class GUIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass
@abstractmethod
def create_checkbox(self) -> Checkbox:
pass
# ============ CONCRETE FACTORIES ============
class WindowsFactory(GUIFactory):
def create_button(self) -> Button:
return WindowsButton()
def create_checkbox(self) -> Checkbox:
return WindowsCheckbox()
class MacFactory(GUIFactory):
def create_button(self) -> Button:
return MacButton()
def create_checkbox(self) -> Checkbox:
return MacCheckbox()
class LinuxFactory(GUIFactory):
def create_button(self) -> Button:
return LinuxButton()
def create_checkbox(self) -> Checkbox:
return LinuxCheckbox()
# ============ CLIENT ============
class Application:
def __init__(self, factory: GUIFactory):
self.factory = factory
def render(self) -> str:
button = self.factory.create_button()
checkbox = self.factory.create_checkbox()
return f"{button.render()}\n{checkbox.render()}"
# ============ REAL-WORLD EXAMPLE: Database Connection ============
class Connection(ABC):
@abstractmethod
def connect(self):
pass
class Query(ABC):
@abstractmethod
def execute(self):
pass
# MySQL
class MySQLConnection(Connection):
def connect(self):
return "Connected to MySQL"
class MySQLQuery(Query):
def execute(self):
return "Executing MySQL query"
# PostgreSQL
class PostgreSQLConnection(Connection):
def connect(self):
return "Connected to PostgreSQL"
class PostgreSQLQuery(Query):
def execute(self):
return "Executing PostgreSQL query"
# MongoDB
class MongoConnection(Connection):
def connect(self):
return "Connected to MongoDB"
class MongoQuery(Query):
def execute(self):
return "Executing MongoDB query"
# Database Factories
class DatabaseFactory(ABC):
@abstractmethod
def create_connection(self) -> Connection:
pass
@abstractmethod
def create_query(self) -> Query:
pass
class MySQLFactory(DatabaseFactory):
def create_connection(self) -> Connection:
return MySQLConnection()
def create_query(self) -> Query:
return MySQLQuery()
class PostgreSQLFactory(DatabaseFactory):
def create_connection(self) -> Connection:
return PostgreSQLConnection()
def create_query(self) -> Query:
return PostgreSQLQuery()
class MongoFactory(DatabaseFactory):
def create_connection(self) -> Connection:
return MongoConnection()
def create_query(self) -> Query:
return MongoQuery()
class Database:
def __init__(self, factory: DatabaseFactory):
self.factory = factory
def setup(self):
connection = self.factory.create_connection()
query = self.factory.create_query()
return f"{connection.connect()}\n{query.execute()}"
# Demo
if __name__ == "__main__":
print("=== GUI Abstract Factory ===")
# Windows GUI
windows_factory = WindowsFactory()
windows_app = Application(windows_factory)
print("Windows Application:")
print(windows_app.render())
print()
# Mac GUI
mac_factory = MacFactory()
mac_app = Application(mac_factory)
print("Mac Application:")
print(mac_app.render())
print()
# Linux GUI
linux_factory = LinuxFactory()
linux_app = Application(linux_factory)
print("Linux Application:")
print(linux_app.render())
print()
print("=== Database Abstract Factory ===")
# MySQL
mysql_db = Database(MySQLFactory())
print("MySQL Setup:")
print(mysql_db.setup())
print()
# PostgreSQL
postgres_db = Database(PostgreSQLFactory())
print("PostgreSQL Setup:")
print(postgres_db.setup())
print()
# MongoDB
mongo_db = Database(MongoFactory())
print("MongoDB Setup:")
print(mongo_db.setup())
Key Concepts
- Abstract Factory: Interface for creating families of products
- Concrete Factory: Creates specific family of related objects
- Family Consistency: Ensures related objects work together
- Product Families: Groups of related objects (Button+Checkbox, Connection+Query)
When to Use
✅ System needs to work with multiple families of related objects
✅ Need consistency among related objects
✅ Want to hide specific classes from clients
✅ Platform-specific implementations (Windows/Mac/Linux)
Interview Q&A
Q1: What's the difference between Factory Method and Abstract Factory?
A: - Factory Method: Creates ONE type of object. Allows subclasses to choose which class. - Abstract Factory: Creates FAMILIES of related objects. Ensures consistency.
# Factory Method (one product)
class Restaurant:
def create_burger(self) -> Burger:
pass
# Abstract Factory (family of products)
class GUIFactory:
def create_button(self) -> Button:
pass
def create_checkbox(self) -> Checkbox:
pass
Q2: How would you handle adding a new product family (e.g., Material Design)?
A: Create new factories without modifying existing code:
class MaterialButton(Button):
def render(self):
return "Rendering Material Design button"
class MaterialCheckbox(Checkbox):
def render(self):
return "Rendering Material Design checkbox"
class MaterialFactory(GUIFactory):
def create_button(self) -> Button:
return MaterialButton()
def create_checkbox(self) -> Checkbox:
return MaterialCheckbox()
# Client code needs NO changes!
factory = MaterialFactory()
app = Application(factory)
Q3: Can you combine Abstract Factory with Singleton?
A: Yes, create single factory instances:
class FactoryProvider:
_factories = {
"windows": WindowsFactory(),
"mac": MacFactory(),
"linux": LinuxFactory()
}
@staticmethod
def get_factory(os_type: str) -> GUIFactory:
return FactoryProvider._factories.get(os_type)
Q4: How would you make factories injectable?
A:
class Application:
def __init__(self, factory: GUIFactory):
self.factory = factory
# Dependency Injection
def create_app(os_type: str) -> Application:
factory = FactoryProvider.get_factory(os_type)
return Application(factory)
# Or in config:
app = Application(config.gui_factory)
Q5: How would you handle factory initialization with parameters?
A:
class ParameterizedFactory(GUIFactory):
def __init__(self, theme: str, size: str):
self.theme = theme
self.size = size
def create_button(self) -> Button:
return Button(theme=self.theme, size=self.size)
# Usage
factory = ParameterizedFactory(theme="dark", size="large")
app = Application(factory)
Trade-offs
✅ Pros: Ensures product family consistency, easy to swap families, isolates concrete classes
❌ Cons: More complex, overkill for simple scenarios, hard to extend
Real-World Examples
- UI frameworks (different platforms need different widgets)
- Database drivers (MySQL vs PostgreSQL vs MongoDB)
- Payment gateways (Stripe, PayPal, Square)
- Cloud providers (AWS, Azure, GCP)
Notes
- Factory Method Pattern employs a single factory class per type, while the Abstract Factory Pattern involves multiple factory methods to manage the creation of interrelated components.