issue 117apr 27mmxxvi
est. 2017
Sun, 27 Apr 2026
vol. IX · no. 117
PapersAdda
placement intelligence, since 2017
640+ briefs · 24 campuses · by reservation
verified offers · sourced from r/developersIndia
razorpay₹65.00 LPA· iit-d · sde-1google₹54.00 LPA· iiit-h · swe-imicrosoft₹49.50 LPA· iit-b · sdeatlassian₹38.00 LPA· nit-w · sde-1amazon₹44.20 LPA· bits-p · sde-1uber₹42.00 LPA· iit-kgp · sde-1razorpay₹65.00 LPA· iit-d · sde-1google₹54.00 LPA· iiit-h · swe-imicrosoft₹49.50 LPA· iit-b · sdeatlassian₹38.00 LPA· nit-w · sde-1amazon₹44.20 LPA· bits-p · sde-1uber₹42.00 LPA· iit-kgp · sde-1

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

11 min read
Interview Questions
Updated: 8 Jun 2026
Aditya Sharma
Aditya's Edit

PapersAdda 2026 Placement Cycle

By Aditya Sharma·Founder & Editor, PapersAdda

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

PatternTypeProblem solved
SingletonCreationalEnsure single instance
FactoryCreationalDecouple object creation from use
BuilderCreationalConstruct complex objects step by step
AdapterStructuralInterface compatibility
DecoratorStructuralAdd behavior without subclassing
FacadeStructuralSimplify complex subsystem
ProxyStructuralControl access to object
ObserverBehavioralNotify multiple dependents on change
StrategyBehavioralSwap algorithms at runtime
CommandBehavioralEncapsulate request, support undo
StateBehavioralBehavior changes with internal state
IteratorBehavioralSequential access without exposing structure
Abstract FactoryCreationalCreate families of related objects
Template MethodBehavioralFixed algorithm skeleton, variable steps
Chain of ResponsibilityBehavioralPass 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.


Methodology applied to this articlelast verified 8 Jun 2026
Sources used
Public exam-pattern documents, official recruiter pages, and verified candidate reports on r/developersIndia and LinkedIn.
Verification window
Page last edited 8 Jun 2026 by Aditya Sharma. Numbers and patterns sanity-checked against the most recent 2026 cycle drives we tracked.
What we did NOT do
  • 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.
Verification policy: /editorial-standards/. Found something incorrect? Submit a correction - we respond within 48 hours.

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

More from PapersAdda

Share this guide: