🧠 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