Skip to content
QuizMaker logoQuizMaker
Activity
Java Backend Interview Prep

No lessons available

CONTENTS

1. Design Patterns for Java Interviews

Review common design patterns and when to apply them in Java backend systems.

Java Backend Interview Prep
1. Java Frameworks & Testing
May 29, 2026
26
A

Why Design Patterns Matter

Design patterns are reusable solutions to recurring design problems. They are not copy-paste code; they are vocabulary for discussing tradeoffs in object-oriented design.

High-Frequency Patterns

PatternIntentJava Interview Example
SingletonEnsure one shared instanceConfiguration registry, logger
AdapterConvert one interface to anotherWrap a legacy payment client behind a new interface
BuilderConstruct complex objects step by stepImmutable request/DTO object with many optional fields
DecoratorAdd behavior without changing the original classLogging, caching, or validation around a service

Pros and Cons

Patterns improve readability when the team recognizes the pattern. They also reduce duplicated design decisions. The risk is overengineering: using a pattern where a simple class would be clearer.

Interview Framing

Say what problem the pattern solves, show a small Java example, then explain the tradeoff. For example, Builder helps avoid huge constructors, but it adds extra code.

Java Code Examples

Singleton

public final class AppConfig {
    private static final AppConfig INSTANCE = new AppConfig();

    private AppConfig() {}

    public static AppConfig getInstance() {
        return INSTANCE;
    }
}

Adapter

interface PaymentGateway {
    void pay(int amount);
}

class LegacyPaymentClient {
    void makePayment(int amountInRupees) {
        System.out.println("Paid " + amountInRupees);
    }
}

class LegacyPaymentAdapter implements PaymentGateway {
    private final LegacyPaymentClient client;

    LegacyPaymentAdapter(LegacyPaymentClient client) {
        this.client = client;
    }

    public void pay(int amount) {
        client.makePayment(amount);
    }
}

Builder

class UserProfile {
    private final String name;
    private final String email;
    private final boolean newsletterEnabled;

    private UserProfile(Builder builder) {
        this.name = builder.name;
        this.email = builder.email;
        this.newsletterEnabled = builder.newsletterEnabled;
    }

    static class Builder {
        private String name;
        private String email;
        private boolean newsletterEnabled;

        Builder name(String name) {
            this.name = name;
            return this;
        }

        Builder email(String email) {
            this.email = email;
            return this;
        }

        Builder newsletterEnabled(boolean enabled) {
            this.newsletterEnabled = enabled;
            return this;
        }

        UserProfile build() {
            return new UserProfile(this);
        }
    }
}

Decorator

interface OrderService {
    void placeOrder(String orderId);
}

class BasicOrderService implements OrderService {
    public void placeOrder(String orderId) {
        System.out.println("Order placed: " + orderId);
    }
}

class LoggingOrderService implements OrderService {
    private final OrderService delegate;

    LoggingOrderService(OrderService delegate) {
        this.delegate = delegate;
    }

    public void placeOrder(String orderId) {
        System.out.println("Before order: " + orderId);
        delegate.placeOrder(orderId);
        System.out.println("After order: " + orderId);
    }
}

Interview Scenario Practice

Scenario 1: Payment SDK Changed

Scenario: A payment provider changed its SDK method names, but your application wants to keep using the same PaymentGateway interface.

Strong answer: Use the Adapter pattern. Keep the application dependent on PaymentGateway, then write an adapter that translates your interface calls into the new SDK calls.

Why it works: The rest of the codebase stays stable while the integration detail is isolated in one class.

Common mistake: Editing every service class to call the new SDK directly. That spreads vendor-specific code everywhere.

Scenario 2: Too Many DTO Constructor Parameters

Scenario: A request DTO has many optional fields and constructors are becoming unreadable.

Strong answer: Use Builder when object creation has many optional values or when readability matters more than a short constructor.

Why it works: Named builder methods make object creation self-documenting and reduce constructor-order mistakes.

Common mistake: Creating multiple overloaded constructors with similar parameter types. That makes bugs easy to miss.

Scenario 3: Add Logging Without Editing Service Code

Scenario: You need to add logging or metrics around an existing service without changing the service class.

Strong answer: Use Decorator. Wrap the original service in another implementation of the same interface and add logging before or after delegating.

Why it works: It follows composition and keeps the original class focused on business behavior.

Common mistake: Putting logging, validation, caching, and business logic into one large service class.

Share this article

Test your knowledge

Take a quick quiz based on this chapter.

mediumJava Backend Interview Prep
Quiz: Design Patterns
11 questions8 min

0 comments

Please login to comment.
No comments yet.
Lesson 1 of 5 in 1. Java Frameworks & Testing
Next in 1. Java Frameworks & Testing
2. Spring Core: IoC, DI, Beans, and Scopes
Back to Java Backend Interview Prep
Back to moduleCategories