目录

12-设计模式

发表于
2 33.6~43.2 分钟 15130

常用的设计模式有哪些?分别解决什么问题?

原始问法:

  • 常用的设计模式有哪些?分别解决什么问题?

来源题目:SRC-12-115-404

面试先答

GoF(四人组)总结了 23 种经典设计模式,分为三大类:创建型(5 种)、结构型(7 种)、行为型(11 种)。面试高频考察的有:单例(保证唯一实例)、工厂(封装对象创建)、代理(控制访问)、观察者(一对多通知)、策略(算法可切换)、模板方法(固定执行框架)、适配器(接口兼容)、装饰器(动态增强)。Spring 框架深度应用了这些模式:IOC 用了工厂和单例,AOP 用了代理,事务用了模板方法。

核心结论
  • 23 种设计模式分三类:创建型、结构型、行为型。
  • 面试高频:单例、工厂、代理、观察者、策略、模板方法。
  • Spring 框架是设计模式的集大成者。
1. 是什么

设计模式是对软件设计中常见问题的可复用解决方案。GoF 的 23 种经典模式:

创建型模式(5 种):关注对象的创建过程

模式 意图 核心问题
单例(Singleton) 保证一个类只有一个实例 如何全局共享唯一实例
工厂方法(Factory Method) 定义创建对象的接口 如何延迟到子类决定创建哪个对象
抽象工厂(Abstract Factory) 创建产品族 如何创建一系列相关对象
建造者(Builder) 分步创建复杂对象 如何简化复杂对象的创建
原型(Prototype) 克隆已有对象 如何高效创建相似对象

结构型模式(7 种):关注类和对象的组合

模式 意图 核心问题
适配器(Adapter) 转换接口使其兼容 如何让不兼容的接口协作
装饰器(Decorator) 动态添加职责 如何在不修改原类的情况下增强功能
外观(Facade) 提供统一入口 如何简化子系统的使用
组合(Composite) 树形结构统一处理 如何统一处理单个和组合对象
代理(Proxy) 控制对象访问 如何间接访问对象
桥接(Bridge) 分离抽象和实现 如何独立变化抽象和实现
享元(Flyweight) 共享细粒度对象 如何高效创建大量相似对象

行为型模式(11 种):关注对象间的通信和职责分配

模式 意图 核心问题
策略(Strategy) 算法族可互换 如何在运行时切换算法
模板方法(Template Method) 固定算法框架 如何让子类实现可变步骤
观察者(Observer) 一对多依赖 如何在状态变化时通知所有依赖
迭代器(Iterator) 顺序访问集合元素 如何不暴露集合内部结构遍历
责任链(Chain of Responsibility) 请求沿链传递 如何让多个处理器都有机会处理
命令(Command) 请求封装为对象 如何支持撤销和排队
中介者(Mediator) 对象间简化通信 如何降低对象间的耦合
备忘录(Memento) 保存/恢复对象状态 如何实现可撤销操作
访问者(Visitor) 分离数据和操作 如何在不修改数据类的情况下添加操作
状态(State) 状态驱动行为变化 如何让状态切换行为
解释器(Interpreter) 定义文法解释器 如何解释特定语言的语句
2. 为什么需要它
  • 复用经过验证的设计方案,避免重复发明轮子。
  • 提高代码的可维护性、可扩展性、可复用性。
  • 降低耦合,提高内聚。
  • 帮助开发者形成统一的设计语言和思维方式。
3. 怎么使用

Spring 中的设计模式应用

Spring 组件 使用的设计模式
BeanFactory 工厂模式 + 单例模式
BeanDefinition 原型模式(克隆创建 Bean)
AOP 代理 代理模式(JDK/CGLIB)
JdbcTemplate 模板方法模式
ApplicationEvent 观察者模式
@Conditional 策略模式
HandlerMapping 策略模式
DispatcherServlet 前端控制器模式
RestTemplate 外观模式
BeanPostProcessor 装饰器模式
4. 适用场景
  • 面试中回答设计模式问题。
  • 日常开发中根据问题选择合适模式。
  • 阅读和理解 Spring 等框架源码。
5. 易错点
  • ❌ 错误:设计模式越多越好。

  • ✅ 正确:设计模式是工具,过度使用会增加复杂度。

  • ❌ 错误:设计模式可以直接套用。

  • ✅ 正确:应根据实际问题选择和调整模式。

一句话总结

23 种经典设计模式分创建型、结构型、行为型三类,面试高频包括单例、工厂、代理、观察者、策略、模板方法,Spring 框架是设计模式的集大成者。


单例模式的实现方式有哪些?应用场景是什么?

原始问法:

  • 单例模式的实现方式有哪些?应用场景是什么?

来源题目:SRC-12-115-405

面试先答

单例模式保证一个类只有一个实例,提供全局访问点。实现方式有六种:1. 饿汉式(静态常量,线程安全但浪费内存);2. 懒汉式(同步方法,线程安全但性能差);3. 双重检查锁(DCL,线程安全且延迟加载,推荐);4. 静态内部类(延迟加载且线程安全,推荐);5. 枚举(最安全,防止反射和序列化破坏);6. 容器单例(Spring Bean 默认)。应用场景:配置中心、日志对象、数据库连接池、缓存、单例工具类。

核心结论
  • 六种实现方式各有优劣,推荐 DCL、静态内部类、枚举。
  • 线程安全是单例模式的关键考量。
  • 需防止反射和序列化破坏单例。
1. 是什么

单例模式(Singleton)是创建型设计模式,保证一个类在系统中只有一个实例,并提供全局访问点。

2. 实现方式

方式一:饿汉式(静态常量)

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

    private Singleton() {}

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

优点:线程安全、实现简单。缺点:类加载时就创建,可能浪费内存。

方式二:懒汉式(同步方法)

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点:线程安全、延迟加载。缺点:每次 getInstance 都同步,性能差。

方式三:双重检查锁(DCL,推荐)

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点:线程安全、延迟加载、性能好。缺点:实现稍复杂。

方式四:静态内部类(推荐)

public class Singleton {
    private Singleton() {}

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

优点:线程安全、延迟加载、实现简洁。缺点:无法传参构造。

方式五:枚举(最安全)

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 业务方法
    }
}

优点:防止反射和序列化破坏。缺点:不够灵活。

方式六:Spring 容器单例

@Service
public class MyService {
    // Spring 默认就是单例
}
3. 应用场景
场景 说明
配置中心 ConfigCenter.getInstance() 全局配置
日志对象 Logger 全局唯一
数据库连接池 DataSource 全局共享
缓存实例 CacheManager 全局缓存
工具类 StringUtilsDateUtils
线程池 全局共享的线程池
4. 防止破坏单例
// 1. 防止反射破坏
private Singleton() {
    if (instance != null) {
        throw new IllegalStateException("Singleton already exists");
    }
}

// 2. 防止序列化破坏
protected Object readResolve() {
    return getInstance();
}
5. 易错点
  • ❌ 错误:饿汉式线程不安全。

  • ✅ 正确:饿汉式通过 JVM 类加载机制保证线程安全。

  • ❌ 错误:单例模式一定是最佳选择。

  • ✅ 正确:单例违反单一职责原则,应谨慎使用。

一句话总结

单例模式有六种实现方式,推荐 DCL、静态内部类和枚举,适用于需要全局唯一实例的场景,需防止反射和序列化破坏。


手写双重检验锁(DCL)实现单例模式?

原始问法:

  • 手写双重检验锁(DCL)实现单例模式?

来源题目:SRC-12-115-406

面试先答

DCL(Double-Checked Locking)实现单例模式的关键:1. 使用 volatile 修饰实例变量(防止指令重排序);2. 第一次 null 检查(避免不必要的同步);3. synchronized 同步块(保证线程安全);4. 第二次 null 检查(防止多线程同时通过第一次检查后重复创建)。DCL 结合了懒加载和线程安全的优点,是面试中最常考的单例实现方式。

核心结论
  • DCL 关键:volatile + 双重 null 检查 + synchronized
  • volatile 防止指令重排序,保证多线程可见性。
  • 两次 null 检查分别解决性能和线程安全问题。
1. 完整实现代码
public class Singleton {

    private static volatile Singleton instance;

    private Singleton() {
        if (instance != null) {
            throw new IllegalStateException("Singleton already exists");
        }
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    protected Object readResolve() {
        return getInstance();
    }
}
2. 关键细节解析

为什么使用 volatile

// instance = new Singleton() 实际分为三步:
// 1. 分配内存空间
// 2. 初始化对象
// 3. 指向内存地址

// 没有 volatile 可能发生指令重排序:
// 1. 分配内存空间
// 3. 指向内存地址  ← 此时 instance 不为 null
// 2. 初始化对象  ← 但对象还未初始化

// 线程 A 执行到 3,线程 B 检查 instance != null
// 返回未初始化的对象 → NPE

第一次 null 检查的作用

if (instance == null) {  // 第一次检查
    // 如果实例已存在,直接返回,避免进入同步块
    // 解决性能问题:只在第一次创建时同步
}

第二次 null 检查的作用

synchronized (Singleton.class) {
    if (instance == null) {  // 第二次检查
        // 多个线程可能同时通过第一次检查
        // 进入同步块后必须再次检查
        instance = new Singleton();
    }
}
3. 执行流程图解
线程 A                          线程 B
  │                               │
  ├─ instance == null? YES        │
  │                               ├─ instance == null? YES
  ├─ enter synchronized           │  (等待锁释放)
  │                               │
  ├─ second null check? YES       │
  │                               │
  ├─ new Singleton()              │
  │                               │
  ├─ instance = new instance      │
  │                               │
  ├─ exit synchronized             │
  │                               ├─ enter synchronized
  │                               │
  │                               ├─ second null check? NO
  │                               │  (instance already created)
  │                               │
  │                               ├─ exit synchronized
  │                               │
  │                               ├─ return instance
  │                               │
  ├─ return instance
4. 优缺点
优点 缺点
线程安全 实现稍复杂
延迟加载 需理解 volatile 和指令重排
性能优秀(大部分时间无锁) JDK 1.4 及以下版本有问题
5. 易错点
  • ❌ 错误:可以用 static synchronized 方法替代。

  • ✅ 正确:static synchronized 每次调用都同步,性能差。

  • ❌ 错误:去掉 volatile 不影响正确性。

  • ✅ 正确:没有 volatile 可能因指令重排导致获取未初始化的对象。

一句话总结

DCL 单例通过 volatile 防止指令重排、双重 null 检查兼顾性能和线程安全、synchronized 保证原子性,是面试中最经典的单例实现。


工厂模式的应用场景是什么?

原始问法:

  • 工厂模式的应用场景是什么?

来源题目:SRC-12-115-407

面试先答

工厂模式(Factory Pattern)是创建型设计模式,核心是将对象的创建与使用分离。分为三种:简单工厂(一个工厂创建多种产品)、工厂方法(每个产品对应一个工厂)、抽象工厂(创建产品族)。应用场景:1. 对象创建逻辑复杂(需要条件判断、资源初始化);2. 需要创建多种类型的相关对象;3. 需要解耦创建和使用;4. 不确定未来需要扩展哪些产品类型。Spring 的 BeanFactory 就是工厂模式的经典应用。

核心结论
  • 三种工厂模式:简单工厂、工厂方法、抽象工厂,复杂度递增。
  • 核心价值:解耦创建和使用、降低耦合、提高可扩展性。
  • Spring BeanFactory 是工厂模式的典范。
1. 三种工厂模式

简单工厂

public class ShapeFactory {
    public Shape getShape(String type) {
        switch (type) {
            case "circle": return new Circle();
            case "rectangle": return new Rectangle();
            default: throw new IllegalArgumentException();
        }
    }
}

工厂方法

public interface ShapeFactory {
    Shape createShape();
}

public class CircleFactory implements ShapeFactory {
    @Override
    public Shape createShape() { return new Circle(); }
}

public class RectangleFactory implements ShapeFactory {
    @Override
    public Shape createShape() { return new Rectangle(); }
}

抽象工厂

public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

public class WindowsFactory implements GUIFactory {
    @Override public Button createButton() { return new WindowsButton(); }
    @Override public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}

public class MacFactory implements GUIFactory {
    @Override public Button createButton() { return new MacButton(); }
    @Override public Checkbox createCheckbox() { return new MacCheckbox(); }
}
2. 应用场景
场景 工厂类型 示例
日志框架 简单工厂 LoggerFactory.getLogger()
数据库连接 简单工厂 DriverManager.getConnection()
Spring Bean 创建 工厂方法 BeanFactory.getBean()
跨平台 UI 抽象工厂 不同操作系统的 UI 组件
支付渠道 抽象工厂 微信/支付宝/银联支付
序列化方式 工厂方法 JSON/XML/Protobuf 序列化
3. 优缺点
类型 优点 缺点
简单工厂 简单易用 违反开闭原则,新增产品需修改工厂
工厂方法 符合开闭原则 类爆炸,每种产品需一个工厂
抽象工厂 创建产品族 增加新产品需修改所有工厂接口
4. 易错点
  • ❌ 错误:工厂模式就是工厂方法模式。

  • ✅ 正确:工厂模式是统称,包含简单工厂、工厂方法、抽象工厂三种。

  • ❌ 错误:工厂模式增加了代码复杂度,不如直接 new。

  • ✅ 正确:工厂模式在创建逻辑复杂或需要解耦时非常有价值。

一句话总结

工厂模式将对象创建与使用分离,三种模式复杂度递增,适用于创建逻辑复杂、需要解耦、多产品类型的场景。


代理模式的应用场景是什么?

原始问法:

  • 代理模式的应用场景是什么?

来源题目:SRC-12-115-408

面试先答

代理模式(Proxy Pattern)是结构型设计模式,为目标对象提供代理控制访问。分为:静态代理(手动创建代理类)、JDK 动态代理(基于接口)、CGLIB 动态代理(基于类)。应用场景:1. 延迟加载(虚拟代理);2. 权限控制(保护代理);3. 远程调用(远程代理);4. AOP 面向切面编程;5. 事务管理(Spring @Transactional);6. 日志记录、性能监控。Spring AOP 和 MyBatis 接口代理都是代理模式的经典应用。

核心结论
  • 代理模式三种实现:静态代理、JDK 动态代理、CGLIB。
  • 核心价值:在不修改目标对象的情况下增强功能。
  • 应用广泛:AOP、事务、缓存、日志、权限等。
1. 三种代理实现

静态代理

public interface UserService {
    void save();
}

public class UserServiceImpl implements UserService {
    @Override
    public void save() { System.out.println("保存用户"); }
}

public class UserServiceProxy implements UserService {
    private UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void save() {
        System.out.println("前置:权限检查");
        target.save();
        System.out.println("后置:日志记录");
    }
}

JDK 动态代理

public class LogProxyHandler implements InvocationHandler {
    private Object target;

    public LogProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用方法: " + method.getName());
        long start = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("耗时: " + elapsed + "ms");
        return result;
    }

    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new LogProxyHandler(target)
        );
    }
}

CGLIB 动态代理

public class LogInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("调用方法: " + method.getName());
        long start = System.currentTimeMillis();
        Object result = proxy.invokeSuper(obj, args);
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("耗时: " + elapsed + "ms");
        return result;
    }

    public static <T> T createProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new LogInterceptor());
        return (T) enhancer.create();
    }
}
2. 应用场景
场景 代理类型 实际案例
AOP 切面 JDK/CGLIB Spring AOP、事务管理
日志监控 JDK/CGLIB 方法耗时统计、调用链追踪
权限控制 静态代理/RMI 接口权限校验
延迟加载 虚拟代理 Hibernate 懒加载
远程调用 远程代理 RMI、RPC
MyBatis Mapper JDK 动态代理 MyBatis 接口代理
缓存代理 静态代理 方法级缓存
3. JDK vs CGLIB 对比
维度 JDK 动态代理 CGLIB
原理 基于接口 基于类(生成子类)
要求 目标类实现接口 无接口要求
限制 final 类/方法不可代理 final 方法不可代理
性能 JDK 8+ 优秀 略优于 JDK
框架 Spring 默认 Spring 可配置
4. 优缺点
优点 缺点
无侵入增强功能 增加调用链路,调试困难
灵活可组合 动态代理有性能开销
解耦增强逻辑 可能导致循环代理
5. 易错点
  • ❌ 错误:JDK 动态代理可以代理任意类。

  • ✅ 正确:JDK 动态代理只能代理实现了接口的类。

  • ❌ 错误:CGLIB 可以代理所有类。

  • ✅ 正确:CGLIB 不能代理 final 类和 final 方法。

一句话总结

代理模式通过为目标对象创建代理控制访问,JDK 基于接口、CGLIB 基于类,广泛应用于 AOP、事务、日志、权限等横切关注点。


观察者模式的应用场景是什么?

原始问法:

  • 观察者模式的应用场景是什么?

来源题目:SRC-12-115-409

面试先答

观察者模式(Observer Pattern)是行为型设计模式,定义了对象间的一对多依赖关系:当主题(Subject)状态变化时,所有观察者(Observer)自动收到通知。核心角色:Subject(主题/被观察者)、Observer(观察者)、ConcreteSubject(具体主题)、ConcreteObserver(具体观察者)。应用场景:1. 事件驱动系统(事件订阅/发布);2. 消息通知系统(邮件、短信推送);3. 数据绑定(UI 与数据同步);4. Spring 事件机制(ApplicationEvent);5. 消息队列(Kafka、RocketMQ 的发布订阅模式)。

核心结论
  • 观察者模式实现一对多的依赖通知。
  • 核心角色:Subject、Observer、ConcreteSubject、ConcreteObserver。
  • Spring 事件机制、消息队列都基于此模式。
1. 核心结构
Subject(主题/被观察者)
  ├─ 维护观察者列表
  ├─ attach(Observer)    注册观察者
  ├─ detach(Observer)    移除观察者
  └─ notify()            通知所有观察者

Observer(观察者接口)
  └─ update()            接收通知

ConcreteSubject(具体主题)
  └─ 触发状态变化 → notify()

ConcreteObserver(具体观察者)
  └─ update() 实现具体逻辑
2. 代码实现
// 主题接口
public interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
}

// 观察者接口
public interface Observer {
    void update(String event);
}

// 具体主题:事件发布器
public class EventBus implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String event;

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(event);
        }
    }

    public void publish(String event) {
        this.event = event;
        notifyObservers();
    }
}

// 具体观察者 A
public class EmailNotifier implements Observer {
    @Override
    public void update(String event) {
        System.out.println("发送邮件通知: " + event);
    }
}

// 具体观察者 B
public class SmsNotifier implements Observer {
    @Override
    public void update(String event) {
        System.out.println("发送短信通知: " + event);
    }
}
3. 应用场景
场景 Subject Observer 实际案例
事件系统 EventBus 各事件处理器 Guava EventBus
Spring 事件 ApplicationEvent ApplicationListener Spring 容器事件
消息队列 Topic Consumer Kafka、RocketMQ
数据绑定 DataModel UI 组件 Vue 双向绑定
订单通知 OrderService 邮件/短信/积分 电商订单状态变更
配置变更 ConfigService 缓存/服务实例 配置中心推送
4. Spring 中的观察者模式
// 自定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    public Order getOrder() { return order; }
}

// 事件监听器
@Component
public class OrderEventListener {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        // 处理订单创建事件
        sendEmail(event.getOrder());
        updatePoints(event.getOrder());
    }
}

// 发布事件
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void createOrder(Order order) {
        // 创建订单逻辑...
        publisher.publishEvent(new OrderCreatedEvent(this, order));
    }
}
5. 优缺点
优点 缺点
解耦主题和观察者 通知顺序不确定
支持一对多广播 可能导致循环通知
动态添加/移除观察者 观察者过多影响性能
6. 易错点
  • ❌ 错误:观察者模式等同于发布订阅模式。

  • ✅ 正确:观察者模式是同步直接通知,发布订阅模式通常引入中间件(消息队列)。

  • ❌ 错误:观察者只能同步通知。

  • ✅ 正确:可以使用异步通知(如 @EventListener(async = true))。

一句话总结

观察者模式实现一对多的依赖通知,当主题状态变化时所有观察者自动收到通知,Spring 事件机制和消息队列都基于此模式。


你的项目中使用了哪些设计模式?为什么选择这些模式?

原始问法:

  • 你的项目中使用了哪些设计模式?为什么选择这些模式?

来源题目:SRC-12-115-410

面试先答

在项目中,我使用了以下设计模式:1. 单例模式:Spring Bean 默认单例,用于 Service、Repository 等无状态服务,保证全局唯一实例、节省资源;2. 工厂模式:使用 Spring BeanFactory 创建 Bean,解耦对象创建和使用;3. 代理模式:Spring AOP 实现日志、事务、权限等横切关注点,MyBatis 接口代理实现数据访问;4. 观察者模式:Spring 事件机制实现订单状态变更通知、配置变更推送;5. 策略模式:支付渠道选择、多种导出格式(Excel/PDF)等需要动态切换算法的场景;6. 模板方法模式:Spring JdbcTemplate 封装数据库操作模板,统一异常处理和资源管理;7. 装饰器模式:MyBatis 插件链、Spring BeanPostProcessor 动态增强功能。选择这些模式的核心考量是:解耦、扩展性、可维护性。

核心结论
  • 项目中最常用的模式:单例、工厂、代理、观察者、策略、模板方法。
  • 选择依据:解耦需求、扩展需求、团队熟悉度。
  • 面试回答要结合具体业务场景,体现模式的价值。
1. 项目实战案例

案例一:支付系统(策略模式 + 工厂模式)

// 支付策略接口
public interface PaymentStrategy {
    PaymentResult pay(PaymentRequest request);
    String getChannel();
}

// 微信支付策略
@Component("WECHAT")
public class WechatPaymentStrategy implements PaymentStrategy { ... }

// 支付宝支付策略
@Component("ALIPAY")
public class AlipayPaymentStrategy implements PaymentStrategy { ... }

// 工厂 + 策略选择
@Service
public class PaymentFactory {
    private Map<String, PaymentStrategy> strategyMap;

    public PaymentFactory(List<PaymentStrategy> strategies) {
        this.strategyMap = strategies.stream()
                .collect(Collectors.toMap(PaymentStrategy::getChannel, s -> s));
    }

    public PaymentStrategy getStrategy(String channel) {
        return strategyMap.get(channel);
    }
}

案例二:订单通知(观察者模式)

// 订单状态变更事件
public class OrderStatusEvent extends ApplicationEvent {
    private final Order order;
    private final OrderStatus status;
}

// 短信通知监听器
@Component
public class SmsNotificationListener {
    @Async
    @EventListener
    public void onOrderStatusChanged(OrderStatusEvent event) {
        // 发送短信
    }
}

// 积分更新监听器
@Component
public class PointsUpdateListener {
    @EventListener
    public void onOrderStatusChanged(OrderStatusEvent event) {
        // 更新积分
    }
}

案例三:数据导出(模板方法模式)

// 导出模板
public abstract class DataExporter {

    public void export(ExportRequest request) {
        // 固定流程:查询→转换→写入→通知
        List<?> data = queryData(request);
        List<?> rows = convertData(data);
        writeToFile(rows, request);
        notifyCompletion(request);
    }

    protected abstract List<?> queryData(ExportRequest request);
    protected abstract List<?> convertData(List<?> data);
    protected abstract void writeToFile(List<?> rows, ExportRequest request);

    protected void notifyCompletion(ExportRequest request) {
        // 默认实现,子类可覆盖
    }
}

// Excel 导出器
@Component
public class ExcelExporter extends DataExporter { ... }

// PDF 导出器
@Component
public class PdfExporter extends DataExporter { ... }
2. 模式选择决策矩阵
需求 选择的模式 原因
全局唯一实例 单例 节省资源、状态一致
对象创建复杂 工厂 解耦创建和使用
横切关注点 代理/AOP 无侵入增强
一对多通知 观察者 解耦事件和处理
算法可切换 策略 运行时灵活选择
固定流程可变步骤 模板方法 统一流程、差异化实现
动态增强 装饰器 灵活组合
接口兼容 适配器 复用现有代码
3. 回答框架

面试回答"项目中使用了哪些设计模式"的建议结构:

  1. 开场白:概览使用的模式数量和种类。
  2. 逐个展开:每个模式说明"是什么→为什么用→怎么用→效果"。
  3. 重点突出:选择 2-3 个最核心的模式详细说明。
  4. 量化成果:说明使用模式带来的收益(如代码量减少、扩展性提升)。
  5. 反思总结:说明模式的权衡和改进空间。
4. 易错点
  • ❌ 错误:模式用得越多越好。

  • ✅ 正确:模式是解决问题的手段,过度使用反而增加复杂度。

  • ❌ 错误:每个模式都要自己实现。

  • ✅ 正确:很多模式(单例、工厂、代理)Spring 已经提供,直接使用即可。

一句话总结

项目中最常用的设计模式是单例、工厂、代理、观察者、策略、模板方法,选择依据是解耦需求和扩展需求,回答时要结合具体业务场景和量化收益。


推荐文章

01-Java基础
11-Spring
10-微服务
上一篇 01-Java基础