Part 1: Creational Design Patterns
3 min read
Lesson slides
1 / 14
Purpose: Deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.
1.1 Singleton Pattern
Intuition: Ensures a class has only one instance and provides global access to it.
classDiagram
class Singleton {
-instance: Singleton
+getInstance(): Singleton
+operation()
}
note for Singleton "Only one instance exists"✅ When to Use:
- Database connection pools (expensive to create multiple)
- Logging services (centralized logging)
- Configuration managers (single source of truth)
- Cache implementations (shared memory space)
- Device drivers (hardware access control)
Code Example:
class DatabaseConnection:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = None
return cls._instance
def connect(self):
if self.connection is None:
self.connection = "Connected to Database"
print("Database connection established")
return self.connection
# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True - Same instance1.2 Factory Pattern
Intuition: Creates objects without specifying their exact class. Like a car factory that can produce different car models based on specifications.
classDiagram
class AnimalFactory {
+createAnimal(type: String): Animal
}
class Animal {
<<interface>>
+makeSound()
}
class Dog {
+makeSound()
}
class Cat {
+makeSound()
}
AnimalFactory --> Animal
Dog ..|> Animal
Cat ..|> Animal✅ When to Use:
- When you don't know which exact class to instantiate until runtime
- When creation logic is complex and centralized
- When you want to decouple object creation from usage
- When you have a family of related products
- When you need to enforce creation constraints
Code Example:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass
class Dog(Animal):
def make_sound(self):
return "Woof!"
class Cat(Animal):
def make_sound(self):
return "Meow!"
class AnimalFactory:
@staticmethod
def create_animal(animal_type):
if animal_type.lower() == "dog":
return Dog()
elif animal_type.lower() == "cat":
return Cat()
else:
raise ValueError(f"Unknown animal type: {animal_type}")
# Usage
factory = AnimalFactory()
dog = factory.create_animal("dog")
cat = factory.create_animal("cat")
print(dog.make_sound()) # Woof!
print(cat.make_sound()) # Meow!- Realworld Example : The Factory Pattern is often used in GUI frameworks where different button types (e.g., WindowsButton, MacButton) can be created based on the operating system.
1.3 Builder Pattern
Intuition: Constructs complex objects step by step. Like building a house where you can choose different options for rooms, materials, etc.
classDiagram
class Pizza {
+size: String
+crust: String
+toppings: List
}
class PizzaBuilder {
-pizza: Pizza
+setSize(size: String): PizzaBuilder
+setCrust(crust: String): PizzaBuilder
+addTopping(topping: String): PizzaBuilder
+build(): Pizza
}
class Director {
+makeMargherita(): Pizza
+makePepperoni(): Pizza
}
Director --> PizzaBuilder
PizzaBuilder --> Pizza✅ When to Use:
- Creating objects with many optional parameters (>4-5 parameters)
- When constructor would have too many parameters
- When you want to create different representations of the same object
- When the construction process is complex and multi-step
- When you need immutable objects with many fields
Code Example:
class Pizza:
def __init__(self):
self.size = None
self.crust = None
self.toppings = []
def __str__(self):
return f"{self.size} {self.crust} pizza with {', '.join(self.toppings)}"
class PizzaBuilder:
def __init__(self):
self.pizza = Pizza()
def set_size(self, size):
self.pizza.size = size
return self
def set_crust(self, crust):
self.pizza.crust = crust
return self
def add_topping(self, topping):
self.pizza.toppings.append(topping)
return self
def build(self):
return self.pizza
# Usage
pizza = (PizzaBuilder()
.set_size("Large")
.set_crust("Thin")
.add_topping("Pepperoni")
.add_topping("Mushrooms")
.build())
print(pizza) # Large Thin pizza with Pepperoni, Mushrooms