Python Abstract Factory Design Pattern

1. Definition

The Abstract Factory Design Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

2. Problem Statement

Imagine you're developing a GUI library that should be compatible with multiple operating systems. Each OS has its own look and feel for elements like buttons, checkboxes, and windows. It's impractical to hardcode the creation of each UI element for each OS throughout your application.

3. Solution

Use the Abstract Factory pattern to define an interface for creating families of related objects, then implement concrete factories for each specific family (e.g., Windows UI elements, Linux UI elements, etc.).

4. Real-World Use Cases

1. GUI libraries where various OS or themes have different implementations of UI elements.

2. Toolkits and libraries that offer multiple variations of a set of functionalities, such as cloud provider resources or database connectors.

5. Implementation Steps

1. Define abstract product interfaces for each type of product.

2. Create an abstract factory interface with methods to produce those products.

3. Implement concrete factories for each family of products.

6. Implementation in Python

from abc import ABC, abstractmethod
# Abstract Products
class Button(ABC):
    @abstractmethod
    def render(self):
        pass
class Checkbox(ABC):
    @abstractmethod
    def check(self):
        pass
# Concrete Products: Windows
class WindowsButton(Button):
    def render(self):
        return "Windows Button Rendered"
class WindowsCheckbox(Checkbox):
    def check(self):
        return "Windows Checkbox Checked"
# Concrete Products: Linux
class LinuxButton(Button):
    def render(self):
        return "Linux Button Rendered"
class LinuxCheckbox(Checkbox):
    def check(self):
        return "Linux Checkbox Checked"
# Abstract Factory
class GUIFactory(ABC):
    @abstractmethod
    def create_button(self):
        pass
    @abstractmethod
    def create_checkbox(self):
        pass
# Concrete Factory: Windows
class WindowsFactory(GUIFactory):
    def create_button(self):
        return WindowsButton()
    def create_checkbox(self):
        return WindowsCheckbox()
# Concrete Factory: Linux
class LinuxFactory(GUIFactory):
    def create_button(self):
        return LinuxButton()
    def create_checkbox(self):
        return LinuxCheckbox()
# Client Code
factory = WindowsFactory()
button = factory.create_button()
checkbox = factory.create_checkbox()
print(button.render())
print(checkbox.check())

Output:

Windows Button Rendered
Windows Checkbox Checked

Explanation:

1. We define abstract product interfaces (Button and Checkbox) that declare the desired methods.

2. Concrete implementations (WindowsButton, LinuxButton, etc.) provide the specific behavior for each family of products.

3. The GUIFactory abstract class sets the blueprint for methods that will produce products.

4. Concrete factories (WindowsFactory, LinuxFactory) implement these methods to produce products of their family.

5. The client code, instead of creating objects directly, uses the abstract factory to get products.

7. When to use?

The Abstract Factory Pattern is beneficial when:

1. The system needs to remain independent of how its objects are created, composed, and represented.

2. The system is configured with multiple families of objects.

3. You want to enforce that a set of related objects is always used together.


Comments