The Factory Design Pattern is a creational design pattern used to provide a way to create objects without specifying the exact class of the object that will be created. This promotes loose coupling in code and enhances maintainability and flexibility.
Before Implementing the Factory Pattern
Consider a situation where you need to create different types of objects based on certain conditions. Without the factory pattern, you might end up with code like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Car: def drive(self): return “Driving a car.” class Bike: def drive(self): return “Riding a bike.” # Client Code def get_vehicle(vehicle_type): if vehicle_type == “car”: return Car() elif vehicle_type == “bike”: return Bike() else: raise ValueError(“Unknown vehicle type!”) # Usage vehicle = get_vehicle(“car”) print(vehicle.drive()) |
Drawbacks:
- The
get_vehiclefunction violates the Open/Closed Principle since every time a new vehicle type is added, the function needs to be modified. - The logic for deciding which object to create is hardcoded, making it less flexible.
After Implementing the Factory Pattern
The Factory Pattern encapsulates object creation, separating it from the client code. Here’s how it improves the code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
from abc import ABC, abstractmethod # Abstract Product class Vehicle(ABC): @abstractmethod def drive(self): pass # Concrete Products class Car(Vehicle): def drive(self): return “Driving a car.” class Bike(Vehicle): def drive(self): return “Riding a bike.” # Factory class VehicleFactory: @staticmethod def create_vehicle(vehicle_type): if vehicle_type == “car”: return Car() elif vehicle_type == “bike”: return Bike() else: raise ValueError(“Unknown vehicle type!”) # Usage vehicle = VehicleFactory.create_vehicle(“bike”) print(vehicle.drive()) |
Advantages:
- Encapsulation: The creation logic is hidden inside the
VehicleFactory. - Extensibility: Adding new vehicle types only requires creating a new class and updating the factory.
When to Use the Factory Pattern
The Factory Pattern is particularly useful in the following scenarios:
- Dynamic Object Creation: When the exact type of object to create is determined at runtime.
- Loose Coupling: When you want to decouple object creation logic from client code.
- Single Responsibility Principle: When the responsibility of creating objects is separate from using them.
- Open/Closed Principle: When adding new types should not require modifying existing code.
Identifying the Right Moment to Use the Factory Pattern
Ask yourself these questions:
- Am I hardcoding object creation logic?
- If
if-eliforswitchstatements appear frequently for object creation, it’s a sign to refactor using a factory.
- If
- Does adding a new class require modifying client code?
- If yes, the factory pattern can improve scalability and maintainability.
- Do you foresee adding new types in the future?
- If the answer is yes, the factory provides a flexible way to accommodate growth.
- Does my code violate the Open/Closed Principle?
- If existing logic needs modification for new object types, consider implementing a factory.
Conclusion
The Factory Design Pattern is a powerful tool in object-oriented programming. It simplifies object creation, ensures scalability, and promotes better software design principles. Using it wisely will lead to cleaner, more maintainable code.
