Design patterns appear in coding interviews in two ways: the interviewer asks you to implement a specific pattern, or you’re expected to recognize that a pattern applies to the problem and mention it naturally. Knowing the six patterns below covers the vast majority of what comes up.
Strategy Pattern
Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. The client selects a strategy at runtime without knowing its implementation.
# Python
class Sorter:
def __init__(self, strategy):
self._strategy = strategy
def sort(self, data):
return self._strategy.sort(data)
class QuickSort:
def sort(self, data): ...
class MergeSort:
def sort(self, data): ...
sorter = Sorter(QuickSort())
sorter.sort([3, 1, 2])
When to use: multiple algorithms for the same behavior (sorting, pricing, routing); want to swap implementations at runtime or per-config without branching logic. Also appears as the payment processor pattern: PaymentStrategy with StripeStrategy and PayPalStrategy implementations.
Observer Pattern
Intent: Define a one-to-many dependency so that when one object changes state, all its dependents are notified automatically.
# Python
class EventBus:
def __init__(self):
self._subscribers = defaultdict(list)
def subscribe(self, event_type, handler):
self._subscribers[event_type].append(handler)
def publish(self, event_type, data):
for handler in self._subscribers[event_type]:
handler(data)
bus = EventBus()
bus.subscribe("price_change", send_alert_email)
bus.subscribe("price_change", update_dashboard)
bus.publish("price_change", {"ticker": "AAPL", "price": 189.50})
Push vs pull: in push mode the subject sends the new value to observers (shown above). In pull mode the subject sends a notification and observers fetch the data themselves — useful when observers only need a subset of the data. In system design interviews, Observer is the conceptual basis for event-driven architectures (Kafka topics, webhook callbacks, UI reactivity).
Factory Pattern
Factory Method: define an interface for creating an object, but let subclasses decide which class to instantiate.
# Java-style pseudocode
abstract class NotificationSender {
abstract Notification createNotification(String msg);
void send(String msg) {
Notification n = createNotification(msg);
n.deliver();
}
}
class EmailSender extends NotificationSender {
Notification createNotification(String msg) {
return new EmailNotification(msg);
}
}
Abstract Factory: creates families of related objects. Example: a UIFactory that produces Button, Checkbox, and Dialog — with a DarkThemeFactory and LightThemeFactory as concrete implementations. Use Abstract Factory when the products must be consistent with each other.
Singleton Pattern
Intent: Ensure a class has only one instance and provide a global access point to it.
# Python thread-safe (double-checked locking equivalent via module-level)
class ConfigManager:
_instance = None
_lock = threading.Lock()
@classmethod
def get_instance(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None: # second check inside lock
cls._instance = cls()
return cls._instance
When NOT to use: Singletons introduce hidden global state that makes unit testing hard — you can’t inject a mock. For most cases, create one instance at application startup and pass it via dependency injection instead. Mention this trade-off in interviews; it signals maturity.
Decorator Pattern
Intent: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
# Python — wrapping an HTTP client with logging and retry
class HttpClient:
def get(self, url): ...
class LoggingDecorator:
def __init__(self, client):
self._client = client
def get(self, url):
print(f"GET {url}")
response = self._client.get(url)
print(f"Response: {response.status}")
return response
class RetryDecorator:
def __init__(self, client, retries=3):
self._client = client
self._retries = retries
def get(self, url):
for attempt in range(self._retries):
try:
return self._client.get(url)
except Exception:
if attempt == self._retries - 1:
raise
client = RetryDecorator(LoggingDecorator(HttpClient()))
Java’s InputStream/BufferedInputStream/GZIPInputStream chain is the textbook example. In web frameworks, middleware stacks are a direct application of Decorator.
Command Pattern
Intent: Encapsulate a request as an object, thereby letting you parameterize clients, queue operations, log them, and support undoable operations.
class Command:
def execute(self): ...
def undo(self): ...
class InsertTextCommand(Command):
def __init__(self, doc, position, text):
self.doc = doc
self.position = position
self.text = text
def execute(self):
self.doc.insert(self.position, self.text)
def undo(self):
self.doc.delete(self.position, len(self.text))
class Editor:
def __init__(self):
self.history = []
def run(self, cmd):
cmd.execute()
self.history.append(cmd)
def undo(self):
if self.history:
self.history.pop().undo()
Command appears in system design when discussing collaborative document editors (like Google Docs): each keystroke is a Command object that can be applied, reversed, or transmitted to other clients for operational transform.
Patterns in System Design Interviews
- Strategy: mention when designing a dispatch system, ranking algorithm, or payment flow — “we can abstract the routing logic behind a Strategy interface so we can swap algorithms without touching the core service.”
- Observer: use when explaining event-driven architecture — “downstream services subscribe to order events via Kafka; this is the Observer pattern at the infrastructure level.”
- Command: invoke when discussing undo/redo (collaborative editors) or audit logs — “every mutation is a Command object persisted to an event log, which gives us replay and rollback for free.”
{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”When should you use the Strategy pattern in a coding interview?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Use Strategy when you have multiple algorithms for the same operation and want to swap them at runtime. Classic interview example: a sorting context that can use bubble sort, merge sort, or quick sort depending on input size. Implementation: define a Strategy interface with a single method (e.g., sort(data)), create concrete classes for each algorithm, inject the strategy into the context. Interview signal: mention Strategy when a system needs pluggable behavior – a payment system supporting multiple processors (Stripe, PayPal, Apple Pay), a dispatch system with different matching algorithms, or a pricing engine with multiple discount strategies. Prefer composition over inheritance; Strategy is a clean alternative to if/else chains or switch statements.”}},{“@type”:”Question”,”name”:”How does the Observer pattern work and when do you use it?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Observer defines a one-to-many dependency: when a subject changes state, all registered observers are notified. Implementation: Subject has subscribe(observer), unsubscribe(observer), and notify() methods. Each Observer implements an update(event) method. Push model: subject sends the changed data in the notification. Pull model: subject sends only a reference to itself; observers pull what they need. Use Observer for event-driven systems: UI components reacting to data model changes, stock price alert systems, webhook delivery (the subject is an order, observers are the webhook dispatcher, inventory service, analytics service). In system design interviews, Observer maps to pub/sub architecture – acknowledge this connection when mentioning Kafka or EventBridge.”}},{“@type”:”Question”,”name”:”What is the difference between Factory Method and Abstract Factory?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Factory Method: a single method that subclasses override to decide which class to instantiate. The creator class defines the interface; concrete creators return concrete products. Use when you have one product type but want subclasses to decide the implementation. Abstract Factory: creates families of related objects without specifying concrete classes. Groups multiple factory methods into one interface. Use when you need to create multiple related objects that must work together (e.g., a UIFactory that creates Button, TextField, and Checkbox for either iOS or Android – all components must come from the same platform family). Interview tip: Factory Method = one product, subclass decides. Abstract Factory = multiple related products, all from the same family.”}},{“@type”:”Question”,”name”:”How do you implement a thread-safe Singleton?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Double-checked locking (Java): declare the instance as volatile; check null twice (once outside synchronized block for performance, once inside for safety). Python: use a metaclass or module-level variable (modules are singletons by default in Python). Better alternative in modern code: use dependency injection instead of Singleton – inject the single instance at startup rather than having classes reach out and grab it. Singleton makes unit testing hard because tests cannot swap out the instance. When Singleton is appropriate: logger, configuration manager, connection pool, thread pool. When to avoid: any class that holds mutable state that tests need to control. Mention the DI alternative in interviews to show awareness of the trade-offs.”}},{“@type”:”Question”,”name”:”How do the Decorator and Command patterns differ and when do you use each?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Decorator adds behavior to an object dynamically without changing its interface. Stack decorators to combine features: a base FileWriter wrapped in BufferedWriter wrapped in EncryptedWriter wrapped in LoggedWriter. Each wrapper implements the same interface. Use Decorator for cross-cutting concerns (logging, caching, rate limiting, auth) and when inheritance would cause a combinatorial explosion of subclasses. Command encapsulates a request as an object, enabling parameterization, queuing, logging, and undo/redo. Each command has execute() and undo() methods. Use Command for: text editor operations (each keystroke is a command, Ctrl+Z pops the command stack), task queues (commands are serialized and sent to a worker), database transactions (command log enables rollback). Interview distinction: Decorator wraps behavior around an object; Command wraps a request as an object.”}}]}
Meta coding interviews test OOP design and design patterns. See common questions for Meta interview: object-oriented design and design patterns.
Atlassian coding rounds include OOP design and design patterns. Review patterns for Atlassian interview: design patterns and OOP system design.
Apple coding interviews test design patterns and OOP principles. See patterns for Apple interview: design patterns and object-oriented design.