Abstract Factory Pattern
Intent:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
When to Use:
When your system needs to be independent of how its objects are created, composed, and represented, and you want to ensure consistency among products.
Key Characteristics:
- Creates groups of related objects
- Ensures consistency among products in a family
- Easy to switch between product families
Think of it like a furniture store - if you buy a "Modern" style chair, you'll want a "Modern" style table to match. The Abstract Factory ensures all your furniture comes from the same style family.
Example: Operating System UI Components
Let's say we're building an application that needs to work on different operating systems (Windows, Mac, Linux). Each OS has its own style of UI components (Button, Menu). We want to ensure that if we create a Windows button, we also create Windows menus - all from the same OS family!
Step 1: Define Abstract UI Components
# ui_components_abstract.py from abc import ABC, abstractmethod class ButtonAbstract(ABC): @abstractmethod def render(self): pass # Each OS will implement this differently class MenuAbstract(ABC): @abstractmethod def render(self): pass # Each OS will implement this differently# ui_components_abstract.py from abc import ABC, abstractmethod class ButtonAbstract(ABC): @abstractmethod def render(self): pass # Each OS will implement this differently class MenuAbstract(ABC): @abstractmethod def render(self): pass # Each OS will implement this differently
Step 2: Create Windows UI Components
# windows_ui.py from ui_components_abstract import ButtonAbstract, MenuAbstract class WindowsButton(ButtonAbstract): def render(self): print("[Windows Logo] Rendering Windows-style button (rectangular, blue border)") class WindowsMenu(MenuAbstract): def render(self): print("[Windows Logo] Rendering Windows-style menu (File, Edit, View options)")# windows_ui.py from ui_components_abstract import ButtonAbstract, MenuAbstract class WindowsButton(ButtonAbstract): def render(self): print("[Windows Logo] Rendering Windows-style button (rectangular, blue border)") class WindowsMenu(MenuAbstract): def render(self): print("[Windows Logo] Rendering Windows-style menu (File, Edit, View options)")
Step 3: Create Mac UI Components
# mac_ui.py from ui_components_abstract import ButtonAbstract, MenuAbstract class MacButton(ButtonAbstract): def render(self): print("[Apple Logo] Rendering Mac-style button (rounded corners, smooth design)") class MacMenu(MenuAbstract): def render(self): print("[Apple Logo] Rendering Mac-style menu (top menu bar, elegant design)")# mac_ui.py from ui_components_abstract import ButtonAbstract, MenuAbstract class MacButton(ButtonAbstract): def render(self): print("[Apple Logo] Rendering Mac-style button (rounded corners, smooth design)") class MacMenu(MenuAbstract): def render(self): print("[Apple Logo] Rendering Mac-style menu (top menu bar, elegant design)")
Step 4: Create Linux UI Components
# linux_ui.py from ui_components_abstract import ButtonAbstract, MenuAbstract class LinuxButton(ButtonAbstract): def render(self): print("[Linux Logo] Rendering Linux-style button (minimalist, customizable)") class LinuxMenu(MenuAbstract): def render(self): print("[Linux Logo] Rendering Linux-style menu (application menu, system settings)")# linux_ui.py from ui_components_abstract import ButtonAbstract, MenuAbstract class LinuxButton(ButtonAbstract): def render(self): print("[Linux Logo] Rendering Linux-style button (minimalist, customizable)") class LinuxMenu(MenuAbstract): def render(self): print("[Linux Logo] Rendering Linux-style menu (application menu, system settings)")
Step 5: Create Abstract Factory
# ui_factory_abstract.py from abc import ABC, abstractmethod class UIFactoryAbstract(ABC): @abstractmethod def create_button(self): pass # Each OS factory will create its own button @abstractmethod def create_menu(self): pass # Each OS factory will create its own menu# ui_factory_abstract.py from abc import ABC, abstractmethod class UIFactoryAbstract(ABC): @abstractmethod def create_button(self): pass # Each OS factory will create its own button @abstractmethod def create_menu(self): pass # Each OS factory will create its own menu
Step 6: Create Windows Factory
# windows_factory.py from ui_factory_abstract import UIFactoryAbstract from windows_ui import WindowsButton, WindowsMenu class WindowsFactory(UIFactoryAbstract): def create_button(self): return WindowsButton() def create_menu(self): return WindowsMenu()# windows_factory.py from ui_factory_abstract import UIFactoryAbstract from windows_ui import WindowsButton, WindowsMenu class WindowsFactory(UIFactoryAbstract): def create_button(self): return WindowsButton() def create_menu(self): return WindowsMenu()
Step 7: Create Mac Factory
# mac_factory.py from ui_factory_abstract import UIFactoryAbstract from mac_ui import MacButton, MacMenu class MacFactory(UIFactoryAbstract): def create_button(self): return MacButton() def create_menu(self): return MacMenu()# mac_factory.py from ui_factory_abstract import UIFactoryAbstract from mac_ui import MacButton, MacMenu class MacFactory(UIFactoryAbstract): def create_button(self): return MacButton() def create_menu(self): return MacMenu()
Step 8: Create Linux Factory
# linux_factory.py from ui_factory_abstract import UIFactoryAbstract from linux_ui import LinuxButton, LinuxMenu class LinuxFactory(UIFactoryAbstract): def create_button(self): return LinuxButton() def create_menu(self): return LinuxMenu()# linux_factory.py from ui_factory_abstract import UIFactoryAbstract from linux_ui import LinuxButton, LinuxMenu class LinuxFactory(UIFactoryAbstract): def create_button(self): return LinuxButton() def create_menu(self): return LinuxMenu()
Step 9: Use the Factory (Client Code)
# main.py from windows_factory import WindowsFactory from mac_factory import MacFactory from linux_factory import LinuxFactory def create_ui(factory): """Create a complete UI using the given factory""" print("\n=== Creating UI Components ===") button = factory.create_button() menu = factory.create_menu() button.render() menu.render() # Create Windows UI print("[Windows Logo] Creating Windows UI:") windows_factory = WindowsFactory() create_ui(windows_factory) # Create Mac UI print("\n[Apple Logo] Creating Mac UI:") mac_factory = MacFactory() create_ui(mac_factory) # Create Linux UI print("\n[Linux Logo] Creating Linux UI:") linux_factory = LinuxFactory() create_ui(linux_factory)# main.py from windows_factory import WindowsFactory from mac_factory import MacFactory from linux_factory import LinuxFactory def create_ui(factory): """Create a complete UI using the given factory""" print("\n=== Creating UI Components ===") button = factory.create_button() menu = factory.create_menu() button.render() menu.render() # Create Windows UI print("[Windows Logo] Creating Windows UI:") windows_factory = WindowsFactory() create_ui(windows_factory) # Create Mac UI print("\n[Apple Logo] Creating Mac UI:") mac_factory = MacFactory() create_ui(mac_factory) # Create Linux UI print("\n[Linux Logo] Creating Linux UI:") linux_factory = LinuxFactory() create_ui(linux_factory)
Output:
[Windows Logo] Creating Windows UI: === Creating UI Components === [Windows Logo] Rendering Windows-style button (rectangular, blue border) [Windows Logo] Rendering Windows-style menu (File, Edit, View options) [Apple Logo] Creating Mac UI: === Creating UI Components === [Apple Logo] Rendering Mac-style button (rounded corners, smooth design) [Apple Logo] Rendering Mac-style menu (top menu bar, elegant design) [Linux Logo] Creating Linux UI: === Creating UI Components === [Linux Logo] Rendering Linux-style button (minimalist, customizable) [Linux Logo] Rendering Linux-style menu (application menu, system settings)
✅ Benefits
1. Consistency:
- All UI components come from the same OS family
- No mixing Windows buttons with Mac menus!
2. Easy to Switch:
- Just change the factory to switch entire UI theme
- One line change:
factory = MacFactory()instead ofWindowsFactory()
3. Easy to Extend:
- Want to add a new OS? Just create new components and a new factory
- No need to change existing code
4. Separation of Concerns:
- Client code doesn't know which OS components it's using
- All complexity is hidden in factories
🎯 Key Points
- Abstract Factory creates families of related objects
- Each factory ensures all objects belong to the same family
- Client code works with abstract interfaces, not concrete classes
- Easy to add new families (just add new factory and components)
- Ensures consistency - all components match the same style
🌟 Quick Summary
- Problem: Need to create groups of related objects that must work together
- Solution: Use Abstract Factory to create entire families of objects
- Benefit: Ensures consistency and makes it easy to switch between families
- Example: Operating systems with matching UI components (Windows/Mac/Linux)
🎉 End
The Abstract Factory Pattern helps you create consistent families of objects. It's perfect when you need to ensure all your components match the same style or theme!