🧠 What is Factory Design Pattern?
The Factory Pattern is a creational pattern that provides an interface for creating objects but allows subclasses to decide which class to instantiate.
🔧 When to Use It ?
Use Factory Pattern when:
- When you don’t know ahead of time what class you need
- When you want to delegate object creation to subclasses
- When you want to work with objects through a common interface
🧩 Scenario
You’re building a system which interacts with speak behavior of different Animals. To start with we have three Animals
: Dot
, Cat
and Duck
. All animals implement a speak
method. We will implement a client program which uses these Animal
object to invoke their speak
method.
animal.py
# Direct class implementations
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
class Duck:
def speak(self):
return "Quack!"
client.py
def create_animal(animal_type: str):
if animal_type.lower() == "dog":
return Dog()
elif animal_type.lower() == "cat":
return Cat()
elif animal_type.lower() == "duck":
return Duck()
else:
raise ValueError("Invalid animal type")
# Usage
def main():
# Client needs to know about all concrete classes
dog = Dog()
cat = Cat()
duck = Duck()
# Or using the function
dog2 = create_animal("dog")
print(dog.speak())
print(cat.speak())
print(duck.speak())
❌ Problem in this approach
In the above example you have:
1. Tight Coupling:
Client code is directly dependent on concrete classes :
- Any changes to class structure affect client code
- No common interface enforced
2. Code Maintenance:
When your system grows:
- Adding new animal types requires modifying client code
- No standardization of methods across classes
- Object creation logic is scattered
3. No Abstraction:
Without providing any abstraction to different Animal
classes :
- There is no guarantee that all animals will implement required methods
- Missing interface enforcement
- Harder to ensure consistency
4. Code Duplication:
- Creation logic might be duplicated across different parts of the application
- No centralized point for object creation
Imaging there are different clients using our Animal objects. Each one of them would have to implement their own logic of create_animal
and need to know all type of Animal
objects that are supported.
Factory Pattern Fix: Centralizing object creation logic
key Components
Abstract Product (Animal):
- Defines the interface for products
- Uses ABC (Abstract Base Class) in Python
Concrete Products (Dog, Cat, Duck):
- Implement the abstract product interface
- Represent the actual objects being created
Factory (AnimalFactory):
- Contains the logic for object creation
- Returns appropriate concrete product based on input
classDiagram
class Product {
<<abstract>>
+operation()* abstract
}
class ConcreteProductA {
+operation()
}
class ConcreteProductB {
+operation()
}
class ConcreteProductC {
+operation()
}
class Factory {
+createProduct(type: string) Product
}
Product <|-- ConcreteProductA
Product <|-- ConcreteProductB
Product <|-- ConcreteProductC
Factory ..> Product : creates
Factory ..> ConcreteProductA : creates
Factory ..> ConcreteProductB : creates
Factory ..> ConcreteProductC : creates
animal.py
from abc import ABC, abstractmethod
# Step 1: Create Abstract Product
class Animal(ABC):
@abstractmethod
def speak(self):
pass
# Step 2: Create Concrete Products
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class Duck(Animal):
def speak(self):
return "Quack!"
# Step 3: Create Factory
class AnimalFactory:
@staticmethod
def create_animal(animal_type: str) -> Animal:
if animal_type.lower() == "dog":
return Dog()
elif animal_type.lower() == "cat":
return Cat()
elif animal_type.lower() == "duck":
return Duck()
else:
raise ValueError("Invalid animal type")
client.py
def main():
factory = AnimalFactory()
# Create different animals
dog = factory.create_animal("dog")
cat = factory.create_animal("cat")
duck = factory.create_animal("duck")
# Make them speak
print(dog.speak()) # Output: Woof!
print(cat.speak()) # Output: Meow!
print(duck.speak()) # Output: Quack!
if __name__ == "__main__":
main()
Benefits of Using Factory Pattern Here
- Encapsulation: Object creation logic is centralized
- Flexibility: Easy to add new product types
- Loose coupling: Client code is separated from object creation