
在技术面试中,设计模式是考察程序员基本功和架构思维的重要环节。尤其是在一线互联网企业的面试中,面试官不仅希望你了解模式的定义,更希望听到你如何在实际的高并发、复杂业务场景中应用它们,解决真实的工程问题。
本文将以一个典型的电商支付场景为主线,串联讲解单例、工厂、策略、模板、装饰器、观察者等核心模式,并提供可直接用于面试的工业级代码案例与答题逻辑。
一、单例模式——高并发场景下的唯一实例管理
单例模式的核心目标是:确保一个类在整个 JVM 进程中只有一个实例,且该类自行完成实例化,并向整个系统提供这个唯一实例。
在高并发场景下,单例模式的核心挑战是:
- 线程安全:避免多线程并发创建多个实例;
- 性能优化:减少锁竞争、避免不必要的同步开销;
- 防破坏:防止通过反射、序列化等手段破坏单例唯一性。
以下是单例模式的几种典型实现方式及其高并发视角分析:
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;
}
}
高并发视角分析:
- 核心优势:绝对安全
- 防反射:反射无法创建枚举实例(
Constructor.newInstance()会抛异常);
- 防序列化:枚举的序列化由 JVM 处理,反序列化不会创建新实例;
- 优点:代码极简、线程安全、防破坏;
- 缺点:无法懒加载(枚举类加载时就初始化);
- 适用场景:高并发、对单例唯一性要求极高的场景(如日志收集、全局计数器)。
单例模式典型应用场景汇总
| 单例实现方式 |
典型应用场景(高并发) |
核心原因 |
| 饿汉式 |
支付配置管理器、全局常量池 |
启动必用、资源小、无懒加载需求 |
| DCL |
订单 ID 生成器、数据库连接池、Redis 客户端 |
懒加载、高并发、需动态初始化 |
| 静态内部类 |
分布式锁管理器、缓存管理器 |
懒加载、无锁、代码优雅 |
| 枚举 |
系统日志收集器、全局异常处理器 |
绝对唯一、防破坏、高可靠 |
单例模式总结
- 高并发下单例的核心:线程安全是基础,性能优化(减少锁竞争)是关键,防破坏(反射/序列化)是进阶要求;
- 选型原则:简单场景选饿汉式,懒加载选静态内部类,高性能 + 动态初始化选 DCL,绝对安全选枚举;
- 核心坑点: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. 策略模式总结
- 策略模式核心:封装“可替换的行为/算法”为独立策略类,通过上下文解耦客户端与策略实现,支持动态切换;
- 支付案例升级价值:相比简单工厂,策略模式不仅解决了对象创建问题,还彻底聚焦“支付行为的封装与替换”,新增渠道时无需修改任何业务逻辑,仅需新增策略类;
- 工厂 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. 解决方式
用装饰器模式动态增强接口功能,核心业务逻辑不变:
// 订单服务核心接口
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. 落地价值
订单状态变更代码大幅精简,新增“优惠券发放”观察者时仅需新增类,无需修改订单核心逻辑,通知模块的扩展效率显著提升。
总结:设计模式面试核心逻辑与实战思维
面试中探讨设计模式,其核心在于展现“实战场景锚定、高并发痛点拆解、模式选型逻辑、工业级组合落地”的系统化思维,而非单纯背诵理论。
- 基础层(单例模式):这是面试第一关,需紧扣高并发下的“线程安全、性能优化、防破坏”三大痛点。回答时要先定场景(如配置管理用饿汉式、ID生成用DCL),再讲实现核心(如
volatile的作用),最后点明选型原则与坑点,形成闭环。
- 应用层(工厂+策略模式):面对“多支付渠道设计”等问题,应对比两种模式:工厂模式解决对象创建的耦合,策略模式解决行为算法的封装与替换。通过策略模式升级支付案例,可以彻底消除if-else,展现对“开闭原则”的深刻理解。
- 组合实战层(策略+模板模式):这是区分中级与高级开发的关键。需要清晰地阐明:模板模式用于定义和复用固定业务流程骨架,策略模式用于灵活替换流程中的具体算法。二者结合是支付、下单等复杂模块的工业级实践,能极大提升代码的复用性和扩展性。
- 架构增强层(装饰器+观察者模式):针对高并发下的非业务需求(如日志、限流)和系统解耦需求(如状态通知),这两种模式提供了优雅的解决方案。装饰器实现无侵入的动态功能增强,观察者实现松耦合的事件驱动通知。
核心面试逻辑:设计模式的本质是 “封装变化、复用代码、解耦依赖” 。在Java高并发场景下,选用任何模式都需额外兼顾“安全、性能、扩展性”三大核心。没有最优的模式,只有最适配场景的方案。实战中应善于将多个模式组合使用,以构建健壮、灵活的工业级系统。
通过以上系统化、场景化的梳理与回答,不仅能展示扎实的技术基本功,更能体现清晰的架构思维和解决复杂问题的能力,从而在技术面试中脱颖而出。如果想与更多开发者交流此类技术实战经验,欢迎访问云栈社区进行探讨。