Implementing Factory Design Patterns in Python
Simple Factory Implementation
The Simple Factory pattern acts as a centralized creation mechanism, encapsulating the object instantiation logic behind a single interface. This approach hides the complexities of class initialization from the client, allowing object creation based on specific input parameters or configurations.
import abc
from abc import ABC, abstractmethod
class Asset(ABC):
"""Base class defining the contract for all manufactured assets."""
@abstractmethod
def construct(self):
pass
class Blade(Asset):
def construct(self):
print("Blade manufacturing process initiated.")
class Polearm(Asset):
def construct(self):
print("Polearm manufacturing process initiated.")
class Firearm(Asset):
def construct(self):
print("Firearm manufacturing process initiated.")
class CentralFactory:
@staticmethod
def create_asset(asset_type):
if asset_type == "blade_type":
return Blade()
elif asset_type == "polearm_type":
return Polearm()
elif asset_type == "firearm_type":
return Firearm()
else:
raise ValueError("Invalid asset type requested.")
# Client usage
item_a = CentralFactory.create_asset("blade_type")
item_b = CentralFactory.create_asset("polearm_type")
item_a.construct()
item_b.construct()
While effective for small-scale systems, the Simple Factory pattern presents a scalability issue. Introducing a new product type requires modifying the `create_asset` method within the `CentralFactory` class. This tight coupling violates the Open/Closed Principle, as the factory logic must be altered every time the product inventory expands.
Factory Method Implementation
To resolve the rigidity of the Simple Factory, the Factory Method pattern delegates the creation logic to separate subclasses. This architecture defines a generic interface for object creation but allows subclasses to decide which specific class to instantiate. In this model, a central abstract creator defines the workflow, while concrete creators handle the specific instantiation details.
import abc
from abc import ABC, abstractmethod
class Asset(ABC):
"""Abstract Product"""
@abstractmethod
def construct(self):
pass
class Blade(Asset):
def construct(self):
print("Blade assembly line active.")
class Polearm(Asset):
def construct(self):
print("Polearm assembly line active.")
class Firearm(Asset):
def construct(self):
print("Firearm assembly line active.")
class Workshop(ABC):
"""Abstract Creator"""
@abstractmethod
def fabricate_product(self):
"""Returns the specific Asset instance."""
pass
def client_operation(self):
"""The main workflow method using the product."""
product = self.fabricate_product()
product.construct()
class BladeWorkshop(Workshop):
def fabricate_product(self):
return Blade()
class PolearmWorkshop(Workshop):
def fabricate_product(self):
return Polearm()
class FirearmWorkshop(Workshop):
def fabricate_product(self):
return Firearm()
# Client usage
blade_workshop = BladeWorkshop()
blade_workshop.client_operation()
polearm_workshop = PolearmWorkshop()
polearm_workshop.client_operation()
Structural Decoupling
By utilizing this pattern, the system achieves a clear separation of concerns. The `Asset` abstract class ensures that all manufactured items adhere to a standard interface, defining them as distinct types of hardware. The `Workshop` abstract class acts as the high-level coordinator, establishing that a factory must exist but leaving the specific implementation to concrete subclasses like `BladeWorkshop`.
When a new requirement arises—for example, the introduction of a new type of weapon system—developers only need to create a new `Asset` subclass and a corresponding `Workshop` subclass. There is no need to refactor the existing factory logic. The client code interacts solely with the abstract `Workshop` interface, remaining completely decoupled from the specific concrete classes and their instantiation logic.