Overview of the Factory Pattern In complex software systems, hardcoding object creation leads to rigid, fragile code. The Factory Pattern solves this by separating the process of object creation from the code that uses those objects. This separation is vital for maintaining high cohesion and low coupling. By shifting the responsibility of instantiation to a specialized factory, the main application logic remains agnostic to specific implementation details. If you need to swap a low-quality video codec for a high-quality one, you only change the factory, not the entire workflow. Prerequisites To follow this guide, you should possess a solid understanding of Python fundamentals, specifically **Object-Oriented Programming (OOP)**. Familiarity with **Abstract Base Classes (ABCs)** and the concept of **Inheritance** is required. You should also understand basic software design principles like **Cohesion** and **Coupling**. Key Libraries & Tools * **abc**: The built-in Python module for defining Abstract Base Classes. * **pathlib**: Used for object-oriented filesystem path manipulation. Code Walkthrough First, we define an abstract interface using the `abc` module. This ensures every factory provides the necessary creation methods. ```python from abc import ABC, abstractmethod class ExporterFactory(ABC): @abstractmethod def get_video_exporter(self) -> VideoExporter: """Returns a new video exporter instance.""" @abstractmethod def get_audio_exporter(self) -> AudioExporter: """Returns a new audio exporter instance.""" ``` Next, we implement concrete factories. Each factory represents a specific configuration, such as a "Fast" or "Master" quality setup. ```python class FastExporter(ExporterFactory): def get_video_exporter(self) -> VideoExporter: return H264BaselineVideoExporter() def get_audio_exporter(self) -> AudioExporter: return AACAudioExporter() ``` Finally, we use **Dependency Injection** by passing the factory into the main function. This removes the need for the `main()` function to know about specific exporter classes. ```python def main(factory: ExporterFactory): video_exporter = factory.get_video_exporter() audio_exporter = factory.get_audio_exporter() # Proceed with export logic... ``` Syntax Notes We use **Type Hinting** to specify that the `main` function expects an `ExporterFactory` type. This increases code readability and assists IDEs in providing better autocomplete. The use of the `@abstractmethod` decorator enforces that any subclass *must* implement the defined methods, preventing runtime errors caused by missing functionality. Practical Examples Factories shine in scenarios where objects must be grouped by environment or locale. Consider a **Global Web Shop**: a `UKFactory` might return a `GBP` currency object and a `VAT` tax calculator, while a `USFactory` returns `USD` and `SalesTax` objects. The checkout logic remains identical; only the factory changes. Tips & Gotchas Avoid the "Class Explosion" trap. If your application requires every possible combination of video and audio codecs (e.g., Low Video + High Audio), creating a factory for every permutation is inefficient. In those cases, favor **Composition** and **Dependency Inversion** over the static Factory Pattern to maintain flexibility without cluttering your codebase with dozens of subclasses.
Abstract Factory
Products
- Jul 16, 2021