Design pattern questions test whether you can recognize common structural and behavioral problems and apply established solutions. Interviewers at Google, Amazon, and Meta use them to assess object-oriented design skills and experience with real-world codebases. The most important thing to know: the problem each pattern solves, not just its structure.
Creational Patterns
Singleton
Problem: ensure a class has only one instance and provide a global access point. Use for shared resources: database connection pools, configuration, logging.
import threading
class DatabasePool:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock: # double-checked locking
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if not self._initialized:
self._pool = [] # initialize connection pool
self._initialized = True
# Both calls return the same instance
db1 = DatabasePool()
db2 = DatabasePool()
assert db1 is db2 # True
When NOT to use: Singletons are global state — they make testing difficult (hard to mock), hide dependencies, and introduce coupling. Prefer dependency injection in most cases. Use Singleton only for truly shared system resources.
Factory Method
Problem: create objects without specifying the exact class. Let subclasses decide which class to instantiate.
from abc import ABC, abstractmethod
class Notification(ABC):
@abstractmethod
def send(self, message: str) -> None: ...
class EmailNotification(Notification):
def send(self, message: str) -> None:
print(f"Email: {message}")
class SMSNotification(Notification):
def send(self, message: str) -> None:
print(f"SMS: {message}")
class PushNotification(Notification):
def send(self, message: str) -> None:
print(f"Push: {message}")
def notification_factory(channel: str) -> Notification:
channels = {
"email": EmailNotification,
"sms": SMSNotification,
"push": PushNotification,
}
cls = channels.get(channel)
if not cls:
raise ValueError(f"Unknown channel: {channel}")
return cls()
# Usage: caller does not know or care which class is created
notif = notification_factory("email")
notif.send("Your order shipped!")
Builder
Problem: construct complex objects step by step, especially when there are many optional parameters.
from dataclasses import dataclass, field
from typing import Optional, List
@dataclass
class QueryBuilder:
table: str
_conditions: List[str] = field(default_factory=list)
_order_by: Optional[str] = None
_limit: Optional[int] = None
_columns: List[str] = field(default_factory=lambda: ["*"])
def select(self, *columns: str) -> "QueryBuilder":
self._columns = list(columns)
return self # fluent interface: return self for chaining
def where(self, condition: str) -> "QueryBuilder":
self._conditions.append(condition)
return self
def order_by(self, column: str) -> "QueryBuilder":
self._order_by = column
return self
def limit(self, n: int) -> "QueryBuilder":
self._limit = n
return self
def build(self) -> str:
sql = f"SELECT {
Frequently Asked Questions
What is the most important design pattern to know for interviews?
There is no single most important pattern, but the patterns most commonly tested are: Factory (creating objects without coupling to concrete classes), Observer (event notification, pub-sub), Strategy (interchangeable algorithms), Decorator (adding behavior without subclassing), and Singleton (single instance — usually asked to discuss its tradeoffs). More important than memorizing pattern structure is understanding the problem each pattern solves. Interviewers care whether you can recognize a problem and apply the right pattern, not whether you can recite the Gang of Four definition.
What is the difference between Strategy and Template Method patterns?
Both patterns separate a fixed algorithm skeleton from variable parts. Strategy uses composition: the algorithm is encapsulated in a separate strategy object that is passed to the context at runtime — you can swap strategies without changing the context class. This is the preferred approach (favors composition over inheritance). Template Method uses inheritance: the algorithm skeleton is defined in a base class abstract method, and subclasses override specific steps — the base class controls the flow. Strategy is more flexible and testable; Template Method is simpler when you already have an inheritance hierarchy.
What is the Decorator pattern and how is it different from inheritance?
The Decorator pattern wraps an object with another object that implements the same interface, adding behavior before or after delegating to the wrapped object. Multiple decorators can be stacked to combine behaviors. Unlike inheritance, decorators are composed at runtime rather than compile time, and you can add any combination of behaviors without an explosion of subclasses. Python function decorators are the most common example: @retry, @cache, @log_calls — each wraps the function independently and they compose naturally. The key advantage: Open/Closed Principle — add new behaviors without modifying existing classes.
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is the most important design pattern to know for interviews?",
"acceptedAnswer": {
"@type": "Answer",
"text": "There is no single most important pattern, but the patterns most commonly tested are: Factory (creating objects without coupling to concrete classes), Observer (event notification, pub-sub), Strategy (interchangeable algorithms), Decorator (adding behavior without subclassing), and Singleton (single instance — usually asked to discuss its tradeoffs). More important than memorizing pattern structure is understanding the problem each pattern solves. Interviewers care whether you can recognize a problem and apply the right pattern, not whether you can recite the Gang of Four definition."
}
},
{
"@type": "Question",
"name": "What is the difference between Strategy and Template Method patterns?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Both patterns separate a fixed algorithm skeleton from variable parts. Strategy uses composition: the algorithm is encapsulated in a separate strategy object that is passed to the context at runtime — you can swap strategies without changing the context class. This is the preferred approach (favors composition over inheritance). Template Method uses inheritance: the algorithm skeleton is defined in a base class abstract method, and subclasses override specific steps — the base class controls the flow. Strategy is more flexible and testable; Template Method is simpler when you already have an inheritance hierarchy."
}
},
{
"@type": "Question",
"name": "What is the Decorator pattern and how is it different from inheritance?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The Decorator pattern wraps an object with another object that implements the same interface, adding behavior before or after delegating to the wrapped object. Multiple decorators can be stacked to combine behaviors. Unlike inheritance, decorators are composed at runtime rather than compile time, and you can add any combination of behaviors without an explosion of subclasses. Python function decorators are the most common example: @retry, @cache, @log_calls — each wraps the function independently and they compose naturally. The key advantage: Open/Closed Principle — add new behaviors without modifying existing classes."
}
}
]
}