Crack JD Interview: Master Singleton, Factory, Strategy, Template & Observer Patterns in 20 Minutes

This article offers a systematic, high‑concurrency‑focused walkthrough of core Java design patterns—including Singleton, Factory, Strategy, Template Method, and Observer—complete with code examples, pros and cons, selection guidelines, and interview‑ready explanations to help candidates impress interviewers and secure offers.

Tech Freedom Circle
Tech Freedom Circle
Tech Freedom Circle
Crack JD Interview: Master Singleton, Factory, Strategy, Template & Observer Patterns in 20 Minutes

Introduction

A JD interview question asks candidates to discuss design patterns, focusing on real‑world scenarios, high‑concurrency challenges, and how to demonstrate strong technical "muscle" to interviewers.

Core Logic of Design Patterns

Essence: encapsulate change, reuse code, and decouple dependencies. In high‑concurrency contexts the three pillars are safety, performance, and extensibility.

Singleton Pattern – High‑Concurrency Instance Management

The goal is to guarantee a single instance per JVM, with the class handling its own instantiation.

Eager Initialization (Hungry Singleton)

Core idea: Instance created during class loading; JVM guarantees thread safety.

public class PayConfigManager {
    private static final PayConfigManager INSTANCE = new PayConfigManager();
    private PayConfigManager() { loadPayConfig(); }
    public static PayConfigManager getInstance() { return INSTANCE; }
    public String getAlipayAppId() { return "2026011600000001"; }
    private void loadPayConfig() { System.out.println("加载支付配置完成(饿汉式:类加载时执行)"); }
}

✅ No lock, extremely high performance.

❌ Eager resource allocation may waste memory if unused.

✅ Suitable for small‑resource, always‑needed components (e.g., configuration manager).

Double‑Checked Locking (DCL) Singleton

Core idea: Lazy loading with fine‑grained lock; lock only on first creation.

public class OrderIdGenerator {
    private static volatile OrderIdGenerator INSTANCE;
    private OrderIdGenerator() { initSnowflake(); }
    public static OrderIdGenerator getInstance() {
        if (INSTANCE == null) {
            synchronized (OrderIdGenerator.class) {
                if (INSTANCE == null) { INSTANCE = new OrderIdGenerator(); }
            }
        }
        return INSTANCE;
    }
    public long generateOrderId() { return System.currentTimeMillis() + 100000L; }
    private void initSnowflake() { System.out.println("初始化雪花算法参数(DCL:首次调用getInstance时执行)"); }
}

✅ Lazy loading, high concurrency performance.

❌ Requires volatile to prevent instruction reordering; otherwise half‑initialized instances may appear.

✅ Ideal for large‑resource objects like ID generators or connection pools.

Static Inner‑Class Singleton

Core idea: JVM loads the inner static class only when getInstance() is called, providing lazy loading without explicit synchronization.

public class DistributedLockManager {
    private DistributedLockManager() { initRedisConn(); }
    private static class SingletonHolder { private static final DistributedLockManager INSTANCE = new DistributedLockManager(); }
    public static DistributedLockManager getInstance() { return SingletonHolder.INSTANCE; }
    public boolean acquireLock(String lockKey) { System.out.println("获取分布式锁:" + lockKey); return true; }
    private void initRedisConn() { System.out.println("初始化Redis连接(静态内部类:首次getInstance时执行)"); }
}

✅ Lazy, lock‑free, elegant code.

❌ Cannot pass parameters to the constructor.

✅ Fits scenarios like distributed lock managers or cache managers.

Enum Singleton (Ultimate Safety)

Core idea: Enum guarantees a single instance, prevents reflection and serialization attacks.

public enum SysLogCollector {
    INSTANCE;
    private SysLogCollector() { initLogCollector(); }
    public void collectLog(String log) { System.out.println("收集日志:" + log); }
    private void initLogCollector() { System.out.println("初始化日志收集器(枚举:类加载时执行)"); }
    public static SysLogCollector getInstance() { return INSTANCE; }
}

✅ Absolute safety against reflection and serialization.

❌ No lazy loading; instance created at enum class loading.

✅ Perfect for global log collectors or counters.

Singleton Summary

Thread safety is the foundation; performance hinges on reducing lock contention.

Selection rule: use eager for simple, always‑needed resources; static inner for lazy, lock‑free cases; DCL for high‑performance lazy loading; enum for absolute safety.

Pitfall: DCL must declare volatile to avoid half‑initialized instances.

Factory Pattern – Simple Factory + Factory Method (Multiple Payment Channels)

Problem

Direct if‑else checks for payment type violate the Open‑Closed Principle and tightly couple business logic with payment logic.

Solution

Simple Factory: centralizes creation of payment objects.

Factory Method: each payment channel has its own factory, making extension a matter of adding a new factory class.

public interface Payment { String pay(String orderId, BigDecimal amount); }
public class WxPay implements Payment { public String pay(String orderId, BigDecimal amount) { return "微信支付成功:" + orderId; } }
public class PaymentFactory {
    public static Payment createPayment(String payType) {
        return switch (payType) {
            case "wx" -> new WxPay();
            case "alipay" -> new AliPay();
            case "union" -> new UnionPay();
            default -> throw new IllegalArgumentException("不支持的支付类型");
        };
    }
}
public class OrderService {
    public String payOrder(String orderId, BigDecimal amount, String payType) {
        Payment payment = PaymentFactory.createPayment(payType);
        return payment.pay(orderId, amount);
    }
}

Value

Adding a new channel (e.g., Digital RMB) only requires a new class and one extra case in the factory; development time drops from a day to ten minutes, and code coupling reduces by about 60%.

Strategy Pattern – Encapsulating Multiple Payment Behaviors

Definition

Behavioral pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable.

Encapsulate variation.

Decouple behavior from client.

Support dynamic switching.

Implementation (Payment Example)

public interface PaymentStrategy { String pay(String orderId, BigDecimal amount); String getPayType(); }
public class WxPayStrategy implements PaymentStrategy {
    public String pay(String orderId, BigDecimal amount) { System.out.println("执行微信支付签名、调起支付接口..."); return "微信支付成功:订单号=" + orderId + ",金额=" + amount + "元"; }
    public String getPayType() { return "wx"; }
}
// Similar classes: AliPayStrategy, UnionPayStrategy
public class PaymentContext {
    private static final Map<String, PaymentStrategy> STRATEGY_MAP = new HashMap<>();
    static { STRATEGY_MAP.put("wx", new WxPayStrategy()); STRATEGY_MAP.put("alipay", new AliPayStrategy()); STRATEGY_MAP.put("union", new UnionPayStrategy()); }
    private PaymentStrategy currentStrategy;
    public PaymentContext(String payType) { this.currentStrategy = STRATEGY_MAP.get(payType); if (this.currentStrategy == null) { throw new IllegalArgumentException("不支持的支付类型:" + payType); } }
    public String executePay(String orderId, BigDecimal amount) { return currentStrategy.pay(orderId, amount); }
}
public class OrderService {
    public String payOrder(String orderId, BigDecimal amount, String payType) { PaymentContext ctx = new PaymentContext(payType); return ctx.executePay(orderId, amount); }
}

Adding Digital RMB requires only a new DigitalRMBPayStrategy class and a registration line in the static block; no business logic changes.

Template Method Pattern – Unified Payment Flow (Strategy + Template)

Definition

Behavioral pattern that defines the skeleton of an algorithm in a method, deferring some steps to subclasses.

Fixed workflow (template).

Subclasses implement variable steps.

Implementation (Payment Template)

public abstract class AbstractPaymentTemplate implements PaymentStrategy {
    @Override
    public final String pay(String orderId, BigDecimal amount) {
        if (!validateOrder(orderId, amount)) { return "支付失败:订单不合法(订单号=" + orderId + ")"; }
        String sign = createPaySign(orderId, amount);
        String payResult = callPayApi(orderId, amount, sign);
        recordPayLog(orderId, amount, payResult);
        return assembleResult(orderId, amount, payResult);
    }
    private boolean validateOrder(String orderId, BigDecimal amount) { System.out.println("通用校验:订单号=" + orderId + ",金额=" + amount); return orderId != null && !orderId.isEmpty() && amount.compareTo(BigDecimal.ZERO) > 0; }
    private void recordPayLog(String orderId, BigDecimal amount, String payResult) { System.out.println("通用日志:记录支付日志——订单号=" + orderId + ",金额=" + amount + ",结果=" + payResult); }
    private String assembleResult(String orderId, BigDecimal amount, String payResult) { return getPayType() + "支付:订单号=" + orderId + ",金额=" + amount + ",结果=" + payResult; }
    protected abstract String createPaySign(String orderId, BigDecimal amount);
    protected abstract String callPayApi(String orderId, BigDecimal amount, String sign);
}

Concrete strategies (e.g., WxPayStrategy, AliPayStrategy, UnionPayStrategy) extend AbstractPaymentTemplate and implement the two abstract methods. Combining Strategy (selection) with Template (fixed flow) yields an industrial‑grade payment module where adding a new channel only means adding a subclass.

Decorator Pattern – Non‑Business Enhancements (Logging & Rate Limiting)

public interface OrderService { String createOrder(String userId, BigDecimal amount); }
public class OrderServiceImpl implements OrderService { public String createOrder(String userId, BigDecimal amount) { return "订单创建成功:" + java.util.UUID.randomUUID(); } }
public class LogDecorator implements OrderService {
    private final OrderService target;
    public LogDecorator(OrderService target) { this.target = target; }
    public String createOrder(String userId, BigDecimal amount) {
        logger.info("创建订单入参:userId={}, amount={}", userId, amount);
        String result = target.createOrder(userId, amount);
        logger.info("创建订单出参:{}", result);
        return result;
    }
}
public class RateLimitDecorator implements OrderService {
    private final OrderService target;
    private final RateLimiter rateLimiter = RateLimiter.create(20000);
    public RateLimitDecorator(OrderService target) { this.target = target; }
    public String createOrder(String userId, BigDecimal amount) {
        if (!rateLimiter.tryAcquire()) { throw new RuntimeException("接口限流,请稍后重试"); }
        return target.createOrder(userId, amount);
    }
}
// Client composition
OrderService service = new RateLimitDecorator(new LogDecorator(new OrderServiceImpl()));
service.createOrder("1001", new BigDecimal(200));

Core business code stays ~30 lines; logging and rate‑limiting are composable without modifying the core.

Observer Pattern – Order Status Change Notification

public class OrderSubject {
    private final List<OrderObserver> observers = new ArrayList<>();
    private String orderStatus;
    public void registerObserver(OrderObserver observer) { observers.add(observer); }
    public void setOrderStatus(String status) { this.orderStatus = status; notifyObservers(); }
    private void notifyObservers() { for (OrderObserver o : observers) { o.update(orderStatus); } }
}
public interface OrderObserver { void update(String orderStatus); }
public class StockObserver implements OrderObserver { public void update(String orderStatus) { if ("PAID".equals(orderStatus)) { logger.info("订单已支付,扣减库存"); } } }
public class SmsObserver implements OrderObserver { public void update(String orderStatus) { if ("SHIPPED".equals(orderStatus)) { logger.info("订单已发货,发送短信通知用户"); } } }
public class OrderBizService {
    public void changeOrderStatus(String orderId, String status) {
        OrderSubject subject = new OrderSubject();
        subject.registerObserver(new StockObserver());
        subject.registerObserver(new SmsObserver());
        subject.registerObserver(new PointObserver()); // additional observer example
        subject.setOrderStatus(status);
    }
}

Adding a new observer (e.g., coupon issuance) only requires a new class; the core order code remains untouched, boosting extension efficiency by ~90%.

Interview Takeaways

When discussing design patterns in an interview, follow a three‑step narrative: set the problem (high concurrency or coupling), present the pattern’s core idea and concrete implementation (including code snippets), compare alternatives, and finally highlight trade‑offs and when to choose each variant. Emphasize that patterns are tools to adapt to specific scenarios rather than universal solutions.

design-patternsJavaHigh ConcurrencyTemplate MethodSingletonStrategyFactoryObserver
Tech Freedom Circle
Written by

Tech Freedom Circle

Crazy Maker Circle (Tech Freedom Architecture Circle): a community of tech enthusiasts, experts, and high‑performance fans. Many top‑level masters, architects, and hobbyists have achieved tech freedom; another wave of go‑getters are hustling hard toward tech freedom.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.