Design Patterns Interview Questions 2026: Top 25 Questions [Answered]

What changed in 2026 drives
Mass-recruiter offer letters are flatter for 2026 batch - the 4-5 LPA ASE band has barely budged in three years while inflation eats real wages. Premium tracks (Digital, Pro, Elite, Specialist) are still where the differential lives, and they are entirely test-driven. If you are aiming higher than the default offer, the coding round is not optional pageantry - it is the entire interview.
What I'd actually study for this
- 01Two solid coding-round answers (1 medium-hard DSA each, with edge-case discussion) > five half-baked ones
- 02One real project you can defend end-to-end - file paths, design decisions, and what you would change
- 03One DBMS schema you actually built (not a textbook ER diagram), with at least 3 join-heavy queries written from memory
- 04Three behavioural STAR stories: failure recovered, conflict handled, ownership taken
Where most candidates trip up
The single biggest mistake is treating company-specific guides as primary prep and DSA as secondary. It is the opposite. Mass recruiters use the test as a filter, but premium tracks at every IT services company use coding to allocate offer band. Spend 70% of prep time on DSA + system fundamentals, 20% on company-specific patterns, 10% on HR rehearsal. Reverse that ratio and you collect the default offer.
Editorial commentary by Aditya Sharma · written for PapersAdda · not generated, not aggregated.
Last Updated: June 2026
Why Design Patterns Matter in 2026 Interviews
Candidates report design pattern questions in roughly 25-30% of SDE-2 and senior rounds at product companies. Based on public preparation resources and candidate-reported interview threads, interviewers use design pattern questions to assess code organization judgment: can you recognize when a pattern applies, and do you know which one fits the problem?
The correct approach is not memorizing pattern definitions but understanding the problem each pattern solves.
Creational Patterns
Q1: What is the Singleton pattern and how do you make it thread-safe?
Singleton ensures only one instance of a class exists globally.
# Thread-safe Singleton (Python)
import threading
class Singleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None: # double-checked locking
cls._instance = super().__new__(cls)
return cls._instance
# Verify
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True
When to use: Database connection pool, configuration manager, logger, thread pool.
Pitfalls: Makes testing hard (global state), violates SRP if it manages its own lifecycle, problematic in distributed systems (one instance per JVM, not per system).
Q2: What is the Factory Method pattern?
Factory Method defines an interface for creating objects but lets subclasses decide which class to instantiate.
from abc import ABC, abstractmethod
class Notification(ABC):
@abstractmethod
def send(self, message): pass
class EmailNotification(Notification):
def send(self, message):
print(f"Email: {message}")
class SMSNotification(Notification):
def send(self, message):
print(f"SMS: {message}")
class PushNotification(Notification):
def send(self, message):
print(f"Push: {message}")
class NotificationFactory:
@staticmethod
def create(notification_type):
factories = {
'email': EmailNotification,
'sms': SMSNotification,
'push': PushNotification
}
cls = factories.get(notification_type)
if not cls:
raise ValueError(f"Unknown type: {notification_type}")
return cls()
# Usage
notif = NotificationFactory.create('email')
notif.send("Hello!")
When to use: When you need to create objects whose exact type is determined at runtime.
Q3: Builder pattern vs Constructor overloading?
Builder constructs complex objects step by step, separating construction from representation.
class Pizza:
def __init__(self):
self.size = None
self.crust = None
self.toppings = []
class PizzaBuilder:
def __init__(self):
self.pizza = Pizza()
def size(self, size):
self.pizza.size = size
return self # fluent interface
def crust(self, crust):
self.pizza.crust = crust
return self
def topping(self, topping):
self.pizza.toppings.append(topping)
return self
def build(self):
return self.pizza
# Clean object construction
pizza = (PizzaBuilder()
.size('large')
.crust('thin')
.topping('cheese')
.topping('mushrooms')
.build())
When to use: Object has many optional parameters; constructor overloading becomes unmanageable (telescoping constructor anti-pattern).
Structural Patterns
Q4: Adapter Pattern
Adapter makes incompatible interfaces work together without changing existing code.
# Existing interface
class IndianSocket:
def plugIn(self):
return "220V power"
# Target interface for European devices
class EuropeanDevice:
def connect(self): pass
# Adapter
class SocketAdapter(EuropeanDevice):
def __init__(self, indian_socket):
self.socket = indian_socket
def connect(self):
voltage = self.socket.plugIn()
return f"Adapted: {voltage} -> 110V"
# Usage
device = SocketAdapter(IndianSocket())
print(device.connect())
Q5: Decorator Pattern
Decorator adds behavior to objects dynamically without subclassing.
from abc import ABC, abstractmethod
class Coffee(ABC):
@abstractmethod
def cost(self): pass
@abstractmethod
def description(self): pass
class SimpleCoffee(Coffee):
def cost(self): return 10
def description(self): return "Simple coffee"
class CoffeeDecorator(Coffee):
def __init__(self, coffee):
self.coffee = coffee
class MilkDecorator(CoffeeDecorator):
def cost(self): return self.coffee.cost() + 2
def description(self): return self.coffee.description() + ", milk"
class SugarDecorator(CoffeeDecorator):
def cost(self): return self.coffee.cost() + 1
def description(self): return self.coffee.description() + ", sugar"
# Compose at runtime
coffee = SimpleCoffee()
coffee = MilkDecorator(coffee)
coffee = SugarDecorator(coffee)
print(f"{coffee.description()}: {coffee.cost()}") # Simple coffee, milk, sugar: 13
When to use: Adding optional features to objects. Java I/O streams (BufferedReader wraps FileReader). Python @functools.wraps.
Q6: Proxy Pattern
Proxy controls access to another object, adding security, caching, or lazy loading.
class Image:
def display(self): pass
class RealImage(Image):
def __init__(self, filename):
self.filename = filename
self._load() # expensive operation
def _load(self):
print(f"Loading {self.filename} from disk")
def display(self):
print(f"Displaying {self.filename}")
class ProxyImage(Image):
"""Lazy loading proxy. Real image loaded only when first displayed."""
def __init__(self, filename):
self.filename = filename
self._real = None
def display(self):
if not self._real:
self._real = RealImage(self.filename) # load on first access
self._real.display()
Types: Virtual Proxy (lazy loading), Protection Proxy (access control), Remote Proxy (network calls), Caching Proxy.
Q7: Facade Pattern
Facade provides a simplified interface to a complex subsystem.
class DVDPlayer:
def on(self): print("DVD on")
def play(self, movie): print(f"Playing {movie}")
def off(self): print("DVD off")
class Amplifier:
def on(self): print("Amp on")
def set_volume(self, v): print(f"Volume: {v}")
def off(self): print("Amp off")
class Projector:
def on(self): print("Projector on")
def off(self): print("Projector off")
class HomeTheaterFacade:
"""One method to set up the whole system."""
def __init__(self, dvd, amp, projector):
self.dvd = dvd
self.amp = amp
self.projector = projector
def watch_movie(self, movie):
self.amp.on()
self.amp.set_volume(5)
self.projector.on()
self.dvd.on()
self.dvd.play(movie)
def end_movie(self):
self.dvd.off()
self.projector.off()
self.amp.off()
Behavioral Patterns
Q8: Observer Pattern
Observer defines a one-to-many dependency. When subject changes state, all observers are notified.
class EventEmitter:
def __init__(self):
self._subscribers = {}
def subscribe(self, event, listener):
self._subscribers.setdefault(event, []).append(listener)
def emit(self, event, data=None):
for listener in self._subscribers.get(event, []):
listener(data)
# Usage
emitter = EventEmitter()
emitter.subscribe('payment', lambda d: print(f"Email: Payment of {d}"))
emitter.subscribe('payment', lambda d: print(f"SMS: Payment of {d}"))
emitter.emit('payment', '500 INR')
# Email: Payment of 500 INR
# SMS: Payment of 500 INR
Real-world: MVC (view observes model), event buses, reactive frameworks (RxJS), Kafka publisher-subscriber.
Q9: Strategy Pattern
Strategy defines a family of algorithms, encapsulates each, and makes them interchangeable.
from abc import ABC, abstractmethod
class SortStrategy(ABC):
@abstractmethod
def sort(self, data): pass
class QuickSort(SortStrategy):
def sort(self, data): return sorted(data) # simplified
class MergeSort(SortStrategy):
def sort(self, data): return sorted(data, key=None) # simplified
class Sorter:
def __init__(self, strategy: SortStrategy):
self.strategy = strategy
def sort(self, data):
return self.strategy.sort(data)
# Switch strategy at runtime
sorter = Sorter(QuickSort())
result = sorter.sort([3,1,4,1,5])
sorter.strategy = MergeSort() # swap strategy
When to use: Multiple algorithms for same task; want to select algorithm at runtime. See also: payment strategies, pricing strategies, compression strategies.
Q10: Command Pattern
Command encapsulates a request as an object, enabling undo/redo, queuing, and logging.
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def execute(self): pass
@abstractmethod
def undo(self): pass
class TextEditor:
def __init__(self):
self.text = ""
class InsertCommand(Command):
def __init__(self, editor, text):
self.editor = editor
self.text = text
def execute(self):
self.editor.text += self.text
def undo(self):
self.editor.text = self.editor.text[:-len(self.text)]
class CommandHistory:
def __init__(self):
self.history = []
def execute(self, command):
command.execute()
self.history.append(command)
def undo(self):
if self.history:
self.history.pop().undo()
# Usage
editor = TextEditor()
history = CommandHistory()
history.execute(InsertCommand(editor, "Hello"))
history.execute(InsertCommand(editor, " World"))
print(editor.text) # Hello World
history.undo()
print(editor.text) # Hello
Q11: State Pattern
State allows an object to alter its behavior when its internal state changes.
class TrafficLight:
def __init__(self):
self.state = 'RED'
self.transitions = {'RED': 'GREEN', 'GREEN': 'YELLOW', 'YELLOW': 'RED'}
def change(self):
print(f"Light: {self.state}")
self.state = self.transitions[self.state]
# Each state can also be a class for complex behavior
Q12: Abstract Factory Pattern
Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes. Where a Factory Method creates one product, an Abstract Factory creates a whole family that must be used together.
from abc import ABC, abstractmethod
# Product families: UI components for two themes
class Button(ABC):
@abstractmethod
def render(self): pass
class Checkbox(ABC):
@abstractmethod
def render(self): pass
class LightButton(Button):
def render(self): return "Light button"
class LightCheckbox(Checkbox):
def render(self): return "Light checkbox"
class DarkButton(Button):
def render(self): return "Dark button"
class DarkCheckbox(Checkbox):
def render(self): return "Dark checkbox"
# Abstract factory: one factory per theme guarantees a consistent family
class UIFactory(ABC):
@abstractmethod
def create_button(self): pass
@abstractmethod
def create_checkbox(self): pass
class LightThemeFactory(UIFactory):
def create_button(self): return LightButton()
def create_checkbox(self): return LightCheckbox()
class DarkThemeFactory(UIFactory):
def create_button(self): return DarkButton()
def create_checkbox(self): return DarkCheckbox()
def build_ui(factory: UIFactory):
return factory.create_button().render(), factory.create_checkbox().render()
When to use: When your system must work with multiple families of related products and you must enforce that products from the same family are used together (you never mix a light button with a dark checkbox). The interview distinction to state: Factory Method produces one object via inheritance; Abstract Factory produces a family of objects via composition of a factory object.
Q13: Template Method Pattern
Template Method defines the skeleton of an algorithm in a base class and lets subclasses override specific steps without changing the algorithm's structure.
from abc import ABC, abstractmethod
class DataExporter(ABC):
"""
The export() method is the template: a fixed sequence of steps.
Subclasses fill in the format-specific steps only.
"""
def export(self, data):
validated = self.validate(data)
formatted = self.format(validated) # subclass-specific
return self.write(formatted)
def validate(self, data):
return [row for row in data if row] # shared step
@abstractmethod
def format(self, data): pass # varies per subclass
def write(self, content):
return f"Written {len(content)} bytes" # shared step
class CsvExporter(DataExporter):
def format(self, data):
return "\n".join(",".join(map(str, row)) for row in data)
class JsonExporter(DataExporter):
def format(self, data):
import json
return json.dumps(data)
When to use: When several algorithms share the same overall structure but differ in a few steps. The base class owns the invariant sequence (the "template") and subclasses customize the variable steps. This is inversion of control: the base class calls down into the subclass, not the other way around. It is the backbone of most framework lifecycle hooks.
Q14: Chain of Responsibility Pattern
Chain of Responsibility passes a request along a chain of handlers until one of them handles it. It decouples the sender of a request from its receivers.
class Handler:
def __init__(self, successor=None):
self.successor = successor
def handle(self, request):
if self.successor:
return self.successor.handle(request)
return None
class AuthHandler(Handler):
def handle(self, request):
if not request.get("token"):
return "401 Unauthorized"
return super().handle(request)
class RateLimitHandler(Handler):
def handle(self, request):
if request.get("rate_exceeded"):
return "429 Too Many Requests"
return super().handle(request)
class RouteHandler(Handler):
def handle(self, request):
return f"200 OK -> {request['path']}"
# Build the chain: auth -> rate limit -> route
chain = AuthHandler(RateLimitHandler(RouteHandler()))
print(chain.handle({"token": "abc", "path": "/home"})) # 200 OK -> /home
print(chain.handle({"path": "/home"})) # 401 Unauthorized
When to use: Middleware pipelines (HTTP request processing), event handling where multiple objects might handle an event, approval workflows where a request escalates up a hierarchy. Each handler decides whether to process the request or pass it on, and you can reorder or insert handlers without touching the others.
Pattern Comparison Table
| Pattern | Type | Problem solved |
|---|---|---|
| Singleton | Creational | Ensure single instance |
| Factory | Creational | Decouple object creation from use |
| Builder | Creational | Construct complex objects step by step |
| Adapter | Structural | Interface compatibility |
| Decorator | Structural | Add behavior without subclassing |
| Facade | Structural | Simplify complex subsystem |
| Proxy | Structural | Control access to object |
| Observer | Behavioral | Notify multiple dependents on change |
| Strategy | Behavioral | Swap algorithms at runtime |
| Command | Behavioral | Encapsulate request, support undo |
| State | Behavioral | Behavior changes with internal state |
| Iterator | Behavioral | Sequential access without exposing structure |
| Abstract Factory | Creational | Create families of related objects |
| Template Method | Behavioral | Fixed algorithm skeleton, variable steps |
| Chain of Responsibility | Behavioral | Pass request along a handler chain |
How to Choose the Right Pattern in an Interview
The mistake candidates make is reciting pattern names. Interviewers want to see you derive the pattern from the problem's axis of change. Ask: what is most likely to change in this system, and which pattern absorbs that change without rippling through the code?
If the thing that varies is which algorithm runs, reach for Strategy. If it is which object gets created, reach for Factory or Abstract Factory. If it is the sequence of steps in a shared workflow with a few customizable parts, reach for Template Method. If behavior depends on a lifecycle stage, reach for State. If you need to add responsibilities to individual objects at runtime, reach for Decorator. Naming the axis of change first, then the pattern, is the senior-level signal.
When NOT to Use a Design Pattern
Candidates report that interviewers explicitly ask "are there cases where you would NOT use this pattern?" Based on public preparation resources, the strong answer is: Singleton adds global state and hinders testability; use dependency injection instead unless truly needed. Decorator creates deep nesting that is hard to debug. Factory adds indirection that may be overkill for simple object creation.
Patterns solve recurring problems. If the problem is not recurring or the code is simple, the pattern adds complexity without benefit. "The simplest solution that works" (KISS) is always the first consideration.
Related Articles
Methodology applied to this articlelast verified 8 Jun 2026
- No fabricated salary numbers or success rates. If we quote a range, it's sourced.
- No noun-substituted templates. This article was not generated by swapping company names in a stock prompt.
- No paid placements, sponsored coaching links, or affiliate-shilled course pushes.
Explore this topic cluster
More resources in Interview Questions
Use the category hub to browse similar questions, exam patterns, salary guides, and preparation resources related to this topic.
Paid contributor programme
Sat this this year? Share your story, earn ₹500.
First-person experience reports help future candidates prep smarter. We pay verified contributors ₹500 via UPI per accepted story - with byline.
Submit your story →Ready to practice?
Take a free timed mock test
Put what you learned into practice. Our mock tests match the 2026 pattern with timer, navigator, reveal, and score breakdown. No signup.
Start Free Mock Test →Related Articles
Airbnb Interview Questions 2026: Top Tech, HR & Behavioural Q&As for Freshers
Clearing Airbnb's fresher loop in 2026 comes down to preparing for the exact mix of questions across technical, behavioural,...
Airtel Interview Questions 2026: Top Tech, HR & Behavioural Q&As for Freshers
Clearing Airtel's fresher loop in 2026 comes down to preparing for the exact mix of questions across technical, behavioural,...
AMD Interview Questions 2026: Top Tech, HR & Behavioural Q&As for Freshers
Clearing AMD's fresher loop in 2026 comes down to preparing for the exact mix of questions across technical, behavioural,...
Atlassian Interview Questions 2026: Top Tech, HR & Behavioural Q&As for Freshers
Clearing Atlassian's fresher loop in 2026 comes down to preparing for the exact mix of questions across technical,...
Barclays Interview Questions 2026
_Last verified by [Aditya Sharma](/author/aditya-sharma/) · cross-checked against PapersAdda Hiring Pulse and...
More from PapersAdda
Microsoft Interview Pattern Bank 2026: LRU Cache, OneDrive & AA Round
Top 15 Product Companies Hiring Freshers India 2026: Compensation + Bar + Interview Loop
Accenture Interview Process 2026: Rounds & Prep
Accenture Interview Questions 2026 (with Answers for Freshers)