🧠 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