找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1472

积分

0

好友

191

主题
发表于 昨天 10:34 | 查看: 6| 回复: 0

Java设计模式面试实战解析

在技术面试中,设计模式是考察程序员基本功和架构思维的重要环节。尤其是在一线互联网企业的面试中,面试官不仅希望你了解模式的定义,更希望听到你如何在实际的高并发、复杂业务场景中应用它们,解决真实的工程问题。

本文将以一个典型的电商支付场景为主线,串联讲解单例、工厂、策略、模板、装饰器、观察者等核心模式,并提供可直接用于面试的工业级代码案例与答题逻辑。

一、单例模式——高并发场景下的唯一实例管理

单例模式的核心目标是:确保一个类在整个 JVM 进程中只有一个实例,且该类自行完成实例化,并向整个系统提供这个唯一实例。

在高并发场景下,单例模式的核心挑战是:

  1. 线程安全:避免多线程并发创建多个实例;
  2. 性能优化:减少锁竞争、避免不必要的同步开销;
  3. 防破坏:防止通过反射、序列化等手段破坏单例唯一性。

以下是单例模式的几种典型实现方式及其高并发视角分析:

1. 饿汉式单例(基础案例:天生线程安全,无并发问题)

核心思想:类加载阶段(JVM 的 <clinit> 方法)就完成实例化,由 JVM 保证类加载的线程安全性,无需额外同步。

典型案例:支付系统的 PayConfigManager(支付配置管理器,系统启动即加载,全程唯一)。

/*
 * 饿汉式单例(基础案例)
 * 场景:支付配置管理器——系统唯一的配置中心,加载后全程不变
 */
public class PayConfigManager {
    // 1. 私有静态常量:类加载时直接初始化,JVM保证线程安全
    private static final PayConfigManager INSTANCE = new PayConfigManager();
    // 2. 私有构造器:禁止外部new创建实例
    private PayConfigManager() {
        // 模拟加载支付配置(如支付宝appId、微信商户号等)
        loadPayConfig();
    }
    // 3. 公共静态方法:对外提供唯一实例
    public static PayConfigManager getInstance() {
        return INSTANCE;
    }
    // 业务方法:获取支付配置
    public String getAlipayAppId() {
        return “2026011600000001”;
    }
    // 私有方法:加载配置(仅初始化时执行一次)
    private void loadPayConfig() {
        System.out.println(“加载支付配置完成(饿汉式:类加载时执行)”);
    }
}

高并发视角分析

  • 优点:无锁、性能极高(高并发下无竞争),天生线程安全;
  • 缺点:类加载时就初始化,若实例占用资源大且长期未使用,会造成内存浪费(无法“懒加载”);
  • 适用场景:实例占用资源小、系统启动后必用的场景(如配置管理器、工具类)。

2. 双重检查锁(DCL)单例(核心推荐:懒加载 + 高性能)

核心思想:结合“懒加载”和“细粒度锁”,仅在第一次创建实例时加锁,后续获取实例无锁,平衡“懒加载”和“高并发性能”。

典型案例:高并发订单 ID 生成器(OrderIdGenerator)—— 仅当首次生成订单 ID 时初始化,且全程唯一。

/*
 * 双重检查锁(DCL)单例(核心推荐案例)
 * 场景:高并发订单ID生成器——懒加载,避免系统启动时无用初始化
 */
public class OrderIdGenerator {
    // 1. 私有静态变量:volatile修饰(禁止指令重排,高并发下关键!)
    private static volatile OrderIdGenerator INSTANCE;
    // 2. 私有构造器:禁止外部创建
    private OrderIdGenerator() {
        // 初始化雪花算法参数(仅第一次执行)
        initSnowflake();
    }
    // 3. 公共静态方法:双重检查+锁,仅第一次创建时加锁
    public static OrderIdGenerator getInstance() {
        // 第一次检查:无实例时才进入同步块(避免每次加锁)
        if (INSTANCE == null) {
            // 加锁:仅类对象锁,保证单线程创建
            synchronized (OrderIdGenerator.class) {
                // 第二次检查:防止多线程等待锁后重复创建
                if (INSTANCE == null) {
                    INSTANCE = new OrderIdGenerator();
                }
            }
        }
        return INSTANCE;
    }
    // 业务方法:生成订单ID(高并发下调用,无锁开销)
    public long generateOrderId() {
        // 模拟雪花算法生成ID
        return System.currentTimeMillis() + 100000L;
    }
    // 初始化雪花算法(仅执行一次)
    private void initSnowflake() {
        System.out.println(“初始化雪花算法参数(DCL:首次调用getInstance时执行)”);
    }
}

高并发视角分析

  • 核心要点volatile必须加!原因:new OrderIdGenerator()分为 3 步(分配内存→初始化对象→赋值给 INSTANCE),JVM 可能指令重排(1→3→2),高并发下线程 B 可能拿到“未初始化完成的 INSTANCE”,volatile禁止重排,保证实例初始化完成后才对外可见;
  • 优点:懒加载(用到才初始化)、高并发性能好(仅第一次加锁);
  • 缺点:代码稍复杂,需注意volatile和双重检查的细节;
  • 适用场景:高并发、实例占用资源大、非启动必用的场景(如 ID 生成器、连接池)。

3. 静态内部类单例(优雅实现:懒加载 + 无锁)

核心思想:利用 JVM “静态内部类的加载时机”实现懒加载 —— 外部类加载时,内部类不会加载,仅当调用getInstance()时才加载内部类,由 JVM 保证加载的线程安全。

典型案例:分布式锁管理器(DistributedLockManager)—— 优雅且高性能的懒加载单例。

/*
 * 静态内部类单例(优雅实现案例)
 * 场景:分布式锁管理器——懒加载,无锁开销
 */
public class DistributedLockManager {
    // 1. 私有构造器:禁止外部创建
    private DistributedLockManager() {
        // 初始化Redis连接(仅内部类加载时执行)
        initRedisConn();
    }
    // 2. 静态内部类:仅当getInstance()调用时才加载
    private static class SingletonHolder {
        // JVM保证内部类加载时的线程安全
        private static final DistributedLockManager INSTANCE = new DistributedLockManager();
    }
    // 3. 公共静态方法:获取实例
    public static DistributedLockManager getInstance() {
        return SingletonHolder.INSTANCE;
    }
    // 业务方法:获取分布式锁(高并发下无锁)
    public boolean acquireLock(String lockKey) {
        System.out.println(“获取分布式锁:” + lockKey);
        return true;
    }
    // 初始化Redis连接
    private void initRedisConn() {
        System.out.println(“初始化Redis连接(静态内部类:首次getInstance时执行)”);
    }
}

高并发视角分析

  • 优点:懒加载、无锁(性能和饿汉式一致)、代码优雅(无 DCL 的复杂检查);
  • 缺点:无法通过参数动态初始化实例(构造器无参);
  • 适用场景:高并发、懒加载、无需动态初始化的场景(如分布式锁、缓存管理器)。

4. 枚举单例(终极方案:绝对安全)

核心思想:JVM 天然保证枚举类的实例唯一性(枚举类的构造器由 JVM 调用,且仅调用一次),且天然防反射、防序列化破坏单例。

典型案例:系统日志收集器(SysLogCollector)—— 高并发下绝对安全的单例。

/*
 * 枚举单例(终极安全案例)
 * 场景:系统日志收集器——绝对唯一,防破坏
 */
public enum SysLogCollector {
    // 唯一枚举实例(JVM保证唯一性)
    INSTANCE;
    // 枚举的构造器(默认private,JVM仅调用一次)
    SysLogCollector() {
        // 初始化日志收集器(如连接ELK)
        initLogCollector();
    }
    // 业务方法:收集系统日志(高并发下调用)
    public void collectLog(String log) {
        System.out.println(“收集日志:” + log);
    }
    // 初始化日志收集器
    private void initLogCollector() {
        System.out.println(“初始化日志收集器(枚举:类加载时执行)”);
    }
    // 对外提供实例(也可直接用SysLogCollector.INSTANCE)
    public static SysLogCollector getInstance() {
        return INSTANCE;
    }
}

高并发视角分析

  • 核心优势:绝对安全
    1. 防反射:反射无法创建枚举实例(Constructor.newInstance()会抛异常);
    2. 防序列化:枚举的序列化由 JVM 处理,反序列化不会创建新实例;
  • 优点:代码极简、线程安全、防破坏;
  • 缺点:无法懒加载(枚举类加载时就初始化);
  • 适用场景:高并发、对单例唯一性要求极高的场景(如日志收集、全局计数器)。

单例模式典型应用场景汇总

单例实现方式 典型应用场景(高并发) 核心原因
饿汉式 支付配置管理器、全局常量池 启动必用、资源小、无懒加载需求
DCL 订单 ID 生成器、数据库连接池、Redis 客户端 懒加载、高并发、需动态初始化
静态内部类 分布式锁管理器、缓存管理器 懒加载、无锁、代码优雅
枚举 系统日志收集器、全局异常处理器 绝对唯一、防破坏、高可靠

单例模式总结

  1. 高并发下单例的核心:线程安全是基础,性能优化(减少锁竞争)是关键,防破坏(反射/序列化)是进阶要求;
  2. 选型原则:简单场景选饿汉式,懒加载选静态内部类,高性能 + 动态初始化选 DCL,绝对安全选枚举;
  3. 核心坑点:DCL 单例必须加volatile(禁止指令重排),否则高并发下会出现“半初始化实例”问题。

二、工厂模式(简单工厂 + 工厂方法)—— 多支付渠道场景

1. 实战场景

电商订单支付模块(支持支付宝、微信、银联 3 种支付方式)。

2. 核心问题

若直接在业务代码中用 if-else 判断支付类型,新增渠道(如数字人民币)时需修改核心代码,违反“开闭原则”,且支付逻辑与业务逻辑高度耦合。

3. 解决方式

  • 简单工厂:统一创建不同支付实例,屏蔽创建细节;
  • 工厂方法:每个支付渠道对应独立工厂,扩展新渠道仅需新增工厂类。
// 支付接口
public interface Payment {
    String pay(String orderId, BigDecimal amount);
}
// 微信支付实现
public class WxPay implements Payment {
    @Override
    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);
    }
}

4. 落地价值

新增数字人民币支付渠道时,仅需新增DigitalRMBPay类和工厂中加一行 case,无需修改订单核心逻辑,扩展耗时大幅降低,代码耦合度显著下降。

三、策略模式——多支付渠道行为封装

1. 什么是策略模式?

策略模式(Strategy Pattern)是行为型设计模式的核心模式之一,核心定义:

  • 定义一系列算法(或业务行为),将每个算法封装成独立的类,使它们可以互相替换
  • 算法的变化独立于使用算法的客户端,客户端无需感知算法内部实现;
  • 核心目标:封装变化、解耦行为与使用方、支持动态切换行为

2. 策略模式 vs 工厂模式(关键区别)

维度 工厂模式(创建型) 策略模式(行为型)
核心关注点 对象的创建 行为/算法的封装与替换
解决的问题 避免 new 关键字耦合,统一创建逻辑 避免 if-else 堆砌,支持行为动态切换
客户端交互 客户端请求“创建对象” 客户端选择“执行哪个策略”

3. 支付案例升级为策略模式(完整实现)

核心思路:把“支付宝/微信/银联支付”视为可替换的支付策略,通过“策略上下文”封装策略的选择和执行,彻底消除 if-else,同时支持动态切换支付方式。

步骤 1:定义策略接口(支付策略接口)

import java.math.BigDecimal;
// 支付策略接口(所有支付方式的统一行为规范)
public interface PaymentStrategy {
    // 核心策略方法:执行支付
    String pay(String orderId, BigDecimal amount);
    // 获取策略标识(用于客户端选择策略)
    String getPayType();
}

步骤 2:实现具体支付策略(封装各支付行为)

// 微信支付策略
public class WxPayStrategy implements PaymentStrategy {
    @Override
    public String pay(String orderId, BigDecimal amount) {
        // 模拟微信支付核心逻辑
        System.out.println(“执行微信支付签名、调起支付接口...”);
        return “微信支付成功:订单号=” + orderId + “,金额=” + amount + “元”;
    }
    @Override
    public String getPayType() {
        return “wx”;
    }
}
// 支付宝支付策略
public class AliPayStrategy implements PaymentStrategy {
    @Override
    public String pay(String orderId, BigDecimal amount) {
        System.out.println(“执行支付宝支付验签、调用 alipay.trade.page.pay...”);
        return “支付宝支付成功:订单号=” + orderId + “,金额=” + amount + “元”;
    }
    @Override
    public String getPayType() {
        return “alipay”;
    }
}
// 银联支付策略
public class UnionPayStrategy implements PaymentStrategy {
    @Override
    public String pay(String orderId, BigDecimal amount) {
        System.out.println(“执行银联支付报文加密、调用银联网关...”);
        return “银联支付成功:订单号=” + orderId + “,金额=” + amount + “元”;
    }
    @Override
    public String getPayType() {
        return “union”;
    }
}

步骤 3:创建策略上下文(解耦客户端与策略实现)

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
// 支付策略上下文(核心:封装策略的选择和执行)
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 void switchStrategy(String payType) {
        this.currentStrategy = STRATEGY_MAP.get(payType);
        if (this.currentStrategy == null) {
            throw new IllegalArgumentException(“不支持的支付类型:” + payType);
        }
    }
}

步骤 4:客户端调用(业务层使用)

import java.math.BigDecimal;
// 订单业务层(客户端)
public class OrderService {
    public String payOrder(String orderId, BigDecimal amount, String payType) {
        // 1. 创建策略上下文(只需传入支付类型,无需关心具体策略)
        PaymentContext context = new PaymentContext(payType);
        // 2. 执行支付(上下文封装了策略执行逻辑)
        return context.executePay(orderId, amount);
    }
    // 测试方法
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        // 微信支付
        String wxResult = orderService.payOrder(“ORDER_20260116_001”, new BigDecimal(“99.00”), “wx”);
        System.out.println(wxResult);
        // 支付宝支付
        String aliResult = orderService.payOrder(“ORDER_20260116_002”, new BigDecimal(“199.00”), “alipay”);
        System.out.println(aliResult);
    }
}

运行结果

执行微信支付签名、调起支付接口...
微信支付成功:订单号=ORDER_20260116_001,金额=99.00元
执行支付宝支付验签、调用 alipay.trade.page.pay...
支付宝支付成功:订单号=ORDER_20260116_002,金额=199.00元

4. 新增支付渠道(数字人民币)的扩展方式

策略模式下新增渠道完全符合“开闭原则”,仅需两步:

// 步骤1:新增数字人民币支付策略
public class DigitalRMBPayStrategy implements PaymentStrategy {
    @Override
    public String pay(String orderId, BigDecimal amount) {
        System.out.println(“执行数字人民币支付、调用数币网关...”);
        return “数字人民币支付成功:订单号=” + orderId + “,金额=” + amount + “元”;
    }
    @Override
    public String getPayType() {
        return “digitalRMB”;
    }
}
// 步骤2:在PaymentContext的静态注册表中添加一行
static {
    // 原有策略...
    STRATEGY_MAP.put(“digitalRMB”, new DigitalRMBPayStrategy());
}

客户端调用无需任何修改,直接传入payType="digitalRMB"即可。

5. 策略模式总结

  1. 策略模式核心:封装“可替换的行为/算法”为独立策略类,通过上下文解耦客户端与策略实现,支持动态切换;
  2. 支付案例升级价值:相比简单工厂,策略模式不仅解决了对象创建问题,还彻底聚焦“支付行为的封装与替换”,新增渠道时无需修改任何业务逻辑,仅需新增策略类;
  3. 工厂 vs 策略:工厂模式解决“怎么创建支付对象”,策略模式解决“怎么执行不同支付行为”,实际开发中可结合使用。

四、模板模式——支付流程统一封装(策略+模板组合)

1. 什么是模板模式?

模板模式(Template Method Pattern)是行为型设计模式,核心定义:

  • 定义一个算法的固定骨架(核心流程),将算法中可变的步骤延迟到子类实现
  • 子类可以重写可变步骤,但不能改变算法的整体结构
  • 核心目标:统一通用流程、复用重复代码、隔离可变细节

2. 模板模式 vs 策略模式(关键区别)

维度 模板模式(行为型) 策略模式(行为型)
核心关注点 固定算法骨架,子类填充细节 封装独立算法,客户端替换整个算法
代码复用 强复用(通用流程在父类) 弱复用(算法间无必然复用)
扩展方式 子类继承重写可变步骤 新增策略类实现接口
典型场景 有固定流程的业务(支付、下单) 多套可替换的算法(支付方式、排序)

3. 支付场景为何要结合「策略 + 模板」?

所有支付方式(微信/支付宝/银联)都有固定的核心流程(通用且不可变):

校验订单合法性 → 执行支付签名/验签 → 调用支付接口 → 记录支付日志 → 返回支付结果

但流程中部分步骤的实现是可变的(比如:微信用微信签名算法,支付宝用支付宝验签算法,银联用加密报文)。

  • 模板模式:封装上述固定流程(父类写死通用步骤),把“签名、调用支付接口”等可变步骤抽象成方法,让子类实现;
  • 策略模式:将每个支付方式视为“可替换的策略”,通过上下文统一管理和调用;
  • 结合后价值:既统一了所有支付的核心流程(避免重复写校验、日志等代码),又保留了支付方式的灵活扩展(新增渠道仅需实现子类)。

4. 「策略 + 模板」改造支付案例(完整实现)

步骤 1:定义支付模板抽象类(封装固定流程,暴露可变步骤)

import java.math.BigDecimal;
// 支付模板抽象类(模板模式核心:固定流程,暴露可变步骤)
public abstract class AbstractPaymentTemplate implements PaymentStrategy {
    // 模板方法:固定支付核心流程(不可被重写)
    @Override
    public final String pay(String orderId, BigDecimal amount) {
        // 步骤1:通用校验(所有支付都要做,复用代码)
        boolean isValid = validateOrder(orderId, amount);
        if (!isValid) {
            return “支付失败:订单不合法(订单号=” + orderId + “)”;
        }
        // 步骤2:可变步骤——支付签名/验签(子类实现)
        String sign = createPaySign(orderId, amount);
        // 步骤3:可变步骤——调用具体支付接口(子类实现)
        String payResult = callPayApi(orderId, amount, sign);
        // 步骤4:通用日志记录(所有支付都要做,复用代码)
        recordPayLog(orderId, amount, payResult);
        // 步骤5:返回结果
        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);
}

步骤 2:具体支付策略(继承模板类,实现可变步骤)

// 微信支付策略(继承模板类,实现可变步骤)
public class WxPayStrategy extends AbstractPaymentTemplate {
    @Override
    protected String createPaySign(String orderId, BigDecimal amount) {
        System.out.println(“微信支付:生成签名(appId=wx123456,mchId=123456,orderId=” + orderId + “)”);
        return “WX_SIGN_” + orderId + “_” + amount;
    }
    @Override
    protected String callPayApi(String orderId, BigDecimal amount, String sign) {
        System.out.println(“微信支付:调用统一下单接口(sign=” + sign + “)”);
        return “success”;
    }
    @Override
    public String getPayType() {
        return “wx”;
    }
}
// 支付宝支付策略(继承模板类,实现可变步骤)
public class AliPayStrategy extends AbstractPaymentTemplate {
    @Override
    protected String createPaySign(String orderId, BigDecimal amount) {
        System.out.println(“支付宝支付:生成验签(appId=202601160000,orderId=” + orderId + “)”);
        return “ALI_SIGN_” + orderId + “_” + amount;
    }
    @Override
    protected String callPayApi(String orderId, BigDecimal amount, String sign) {
        System.out.println(“支付宝支付:调用alipay.trade.page.pay接口(sign=” + sign + “)”);
        return “success”;
    }
    @Override
    public String getPayType() {
        return “alipay”;
    }
}
// 银联支付策略(继承模板类,实现可变步骤)
public class UnionPayStrategy extends AbstractPaymentTemplate {
    @Override
    protected String createPaySign(String orderId, BigDecimal amount) {
        System.out.println(“银联支付:报文加密(merId=888888,orderId=” + orderId + “)”);
        return “UNION_SIGN_” + orderId + “_” + amount;
    }
    @Override
    protected String callPayApi(String orderId, BigDecimal amount, String sign) {
        System.out.println(“银联支付:调用银联网关接口(sign=” + sign + “)”);
        return “success”;
    }
    @Override
    public String getPayType() {
        return “union”;
    }
}

步骤 3:策略上下文(复用逻辑,管理策略)

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
// 支付策略上下文(管理策略,解耦客户端)
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);
    }
}

步骤 4:客户端调用(业务层使用)

import java.math.BigDecimal;
// 订单业务层(客户端)
public class OrderService {
    public String payOrder(String orderId, BigDecimal amount, String payType) {
        PaymentContext context = new PaymentContext(payType);
        return context.executePay(orderId, amount);
    }
    // 测试
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        // 微信支付
        String wxResult = orderService.payOrder(“ORDER_20260116_001”, new BigDecimal(“99.00”), “wx”);
        System.out.println(“最终结果:” + wxResult + “\n”);
        // 支付宝支付
        String aliResult = orderService.payOrder(“ORDER_20260116_002”, new BigDecimal(“199.00”), “alipay”);
        System.out.println(“最终结果:” + aliResult);
    }
}

运行结果

通用校验:订单号=ORDER_20260116_001,金额=99.00,校验订单状态、金额非负...
微信支付:生成签名(appId=wx123456,mchId=123456,orderId=ORDER_20260116_001)
微信支付:调用统一下单接口(sign=WX_SIGN_ORDER_20260116_001_99.00)
通用日志:记录支付日志——订单号=ORDER_20260116_001,金额=99.00,结果=success
最终结果:wx支付:订单号=ORDER_20260116_001,金额=99.00,结果=success

通用校验:订单号=ORDER_20260116_002,金额=199.00,校验订单状态、金额非负...
支付宝支付:生成验签(appId=202601160000,orderId=ORDER_20260116_002)
支付宝支付:调用alipay.trade.page.pay接口(sign=ALI_SIGN_ORDER_20260116_002_199.00)
通用日志:记录支付日志——订单号=ORDER_20260116_002,金额=199.00,结果=success
最终结果:alipay支付:订单号=ORDER_20260116_002,金额=199.00,结果=success

5. 新增支付渠道(数字人民币)的扩展方式

结合「策略 + 模板」后,新增渠道仅需新增一个子类(无需修改任何现有代码):

// 数字人民币支付策略(继承模板类,实现可变步骤)
public class DigitalRMBPayStrategy extends AbstractPaymentTemplate {
    @Override
    protected String createPaySign(String orderId, BigDecimal amount) {
        System.out.println(“数字人民币:生成数币签名(appId=DRMB2026,orderId=” + orderId + “)”);
        return “DRMB_SIGN_” + orderId + “_” + amount;
    }
    @Override
    protected String callPayApi(String orderId, BigDecimal amount, String sign) {
        System.out.println(“数字人民币:调用数币网关接口(sign=” + sign + “)”);
        return “success”;
    }
    @Override
    public String getPayType() {
        return “digitalRMB”;
    }
}
// 仅需在上下文注册表中添加一行
static {
    // 原有策略...
    STRATEGY_MAP.put(“digitalRMB”, new DigitalRMBPayStrategy());
}

6. 模板模式总结

  1. 模板模式核心:定义固定业务流程(如支付的“校验→签名→调用接口→日志→返回”),将可变步骤抽象为方法,子类仅需实现可变细节,最大化复用通用代码;
  2. 策略 + 模板结合价值:模板模式统一支付流程、复用通用逻辑,策略模式实现支付方式的灵活替换和扩展,是支付模块的工业级实践;
  3. 扩展规则:新增支付渠道仅需“继承模板类实现可变步骤 + 注册到策略上下文”,完全符合“开闭原则”,无侵入式扩展。

五、装饰器模式——接口增强(日志/限流)场景

1. 实战场景

高并发接口(如订单创建)需添加日志记录、限流、参数校验功能,但不想侵入核心业务代码。

2. 核心问题

若直接在接口内加日志/限流代码,会导致业务逻辑与非业务逻辑耦合,且不同接口的增强需求不同(如部分接口仅需日志,部分需限流)。

3. 解决方式

用装饰器模式动态增强接口功能,核心业务逻辑不变:

// 订单服务核心接口
public interface OrderService {
    String createOrder(String userId, BigDecimal amount);
}
// 核心实现(仅包含业务逻辑)
public class OrderServiceImpl implements OrderService {
    @Override
    public String createOrder(String userId, BigDecimal amount) {
        // 仅处理订单创建核心逻辑
        return “订单创建成功:” + UUID.randomUUID();
    }
}
// 日志装饰器
public class LogDecorator implements OrderService {
    private OrderService target;
    public LogDecorator(OrderService target) {
        this.target = target;
    }
    @Override
    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 OrderService target;
    private RateLimiter rateLimiter = RateLimiter.create(20000); // 2万QPS限流
    public RateLimitDecorator(OrderService target) {
        this.target = target;
    }
    @Override
    public String createOrder(String userId, BigDecimal amount) {
        // 增强逻辑:限流
        if (!rateLimiter.tryAcquire()) {
            throw new RuntimeException(“接口限流,请稍后重试”);
        }
        return target.createOrder(userId, amount);
    }
}
// 调用端(按需组合装饰器)
public class Client {
    public static void main(String[] args) {
        OrderService orderService = new RateLimitDecorator(
            new LogDecorator(new OrderServiceImpl())
        );
        orderService.createOrder(“1001”, new BigDecimal(200));
    }
}

4. 落地价值

核心业务代码保持简洁,日志/限流功能可按需组合,新增“参数校验”装饰器时无需修改核心代码,接口非业务逻辑的复用率大幅提升。

六、观察者模式——订单状态变更通知场景

1. 实战场景

订单状态变更(已支付、已发货、已完成)时,需同步通知库存、物流、积分、短信 4 个模块。

2. 核心问题

若在订单状态变更方法中直接调用 4 个模块的接口,会导致订单代码臃肿,新增通知模块(如优惠券发放)时需修改订单核心逻辑。

3. 解决方式

用观察者模式解耦,订单作为被观察者,通知模块作为观察者,状态变更时自动触发所有观察者逻辑:

// 被观察者(订单)
public class OrderSubject {
    private 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 observer : observers) {
            observer.update(orderStatus);
        }
    }
}
// 观察者接口
public interface OrderObserver {
    void update(String orderStatus);
}
// 库存观察者(订单支付后扣减库存)
public class StockObserver implements OrderObserver {
    @Override
    public void update(String orderStatus) {
        if (“PAID”.equals(orderStatus)) {
            // 扣减库存逻辑
            logger.info(“订单已支付,扣减库存”);
        }
    }
}
// 短信观察者(订单发货后发送短信)
public class SmsObserver implements OrderObserver {
    @Override
    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()); // 积分模块
        // 变更状态,自动通知所有观察者
        subject.setOrderStatus(status);
    }
}

4. 落地价值

订单状态变更代码大幅精简,新增“优惠券发放”观察者时仅需新增类,无需修改订单核心逻辑,通知模块的扩展效率显著提升。

总结:设计模式面试核心逻辑与实战思维

面试中探讨设计模式,其核心在于展现“实战场景锚定、高并发痛点拆解、模式选型逻辑、工业级组合落地”的系统化思维,而非单纯背诵理论。

  1. 基础层(单例模式):这是面试第一关,需紧扣高并发下的“线程安全、性能优化、防破坏”三大痛点。回答时要先定场景(如配置管理用饿汉式、ID生成用DCL),再讲实现核心(如volatile的作用),最后点明选型原则与坑点,形成闭环。
  2. 应用层(工厂+策略模式):面对“多支付渠道设计”等问题,应对比两种模式:工厂模式解决对象创建的耦合,策略模式解决行为算法的封装与替换。通过策略模式升级支付案例,可以彻底消除if-else,展现对“开闭原则”的深刻理解。
  3. 组合实战层(策略+模板模式):这是区分中级与高级开发的关键。需要清晰地阐明:模板模式用于定义和复用固定业务流程骨架,策略模式用于灵活替换流程中的具体算法。二者结合是支付、下单等复杂模块的工业级实践,能极大提升代码的复用性和扩展性。
  4. 架构增强层(装饰器+观察者模式):针对高并发下的非业务需求(如日志、限流)和系统解耦需求(如状态通知),这两种模式提供了优雅的解决方案。装饰器实现无侵入的动态功能增强,观察者实现松耦合的事件驱动通知

核心面试逻辑:设计模式的本质是 “封装变化、复用代码、解耦依赖” 。在Java高并发场景下,选用任何模式都需额外兼顾“安全、性能、扩展性”三大核心。没有最优的模式,只有最适配场景的方案。实战中应善于将多个模式组合使用,以构建健壮、灵活的工业级系统。

通过以上系统化、场景化的梳理与回答,不仅能展示扎实的技术基本功,更能体现清晰的架构思维和解决复杂问题的能力,从而在技术面试中脱颖而出。如果想与更多开发者交流此类技术实战经验,欢迎访问云栈社区进行探讨。




上一篇:PVE存储超配致9台虚拟机瘫痪:紧急扩容与恢复实战
下一篇:Linux内核为何偏爱for(;;)?从汇编、规范到代码风格的深度解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-18 18:29 , Processed in 0.435886 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表