在准备后端面试时,设计模式几乎是绕不开的话题。
很多框架(例如 Spring)内部大量使用了设计模式,因此理解这些模式不仅有助于写业务代码,也能更好地理解框架源码。

设计模式本质上是:

针对常见软件设计问题的通用解决方案。

经典设计模式共有 23 种,通常分为三大类:

类型 作用
创建型模式 解决对象创建问题
结构型模式 解决类或对象组合问题
行为型模式 解决对象之间交互问题

面试中最常问的其实只有几种,下面整理几个高频模式。

一、单例模式(Singleton)

1. 解决的问题

确保某个类在系统中只有一个实例,并提供全局访问方式。

典型场景:

  • 配置管理
  • 日志对象
  • 数据库连接池
  • Spring Bean 默认单例

2. 基本实现(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {

private static Singleton instance;

private Singleton() {}

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

这个版本在多线程环境下不安全。

3. 面试常问:线程安全实现(DCL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}
}

关键点:

  • volatile 防止指令重排
  • 双重检查减少锁开销

4. 更简单写法(推荐)

1
2
3
4
5
6
7
8
9
10
public class Singleton {

private static final Singleton INSTANCE = new Singleton();

private Singleton() {}

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

利用类加载机制保证线程安全,写法也更简洁。

二、工厂模式(Factory)

1. 解决的问题

当对象创建逻辑复杂时,将对象创建过程封装起来。

核心思想:

让对象的创建和使用分离。

2. 简单工厂示例

假设系统支持多种消息发送方式:邮件、短信、推送。

统一接口:

1
2
3
public interface MessageSender {
void send(String message);
}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class EmailSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("发送邮件:" + message);
}
}

public class SmsSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("发送短信:" + message);
}
}

工厂类:

1
2
3
4
5
6
7
8
9
10
11
12
public class MessageFactory {

public static MessageSender create(String type) {
if ("email".equals(type)) {
return new EmailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
throw new IllegalArgumentException("unsupported type: " + type);
}
}

使用方式:

1
2
MessageSender sender = MessageFactory.create("email");
sender.send("hello");

3. 面试常问

简单工厂 vs 工厂方法:

模式 特点
简单工厂 一个工厂类创建所有对象
工厂方法 每种产品对应一个工厂

工厂方法接口示例:

1
2
3
public interface Factory {
MessageSender create();
}

三、策略模式(Strategy)

1. 解决的问题

当一个功能有多种实现方式时,可以在运行时动态选择算法。

核心思想:

将不同算法封装为独立策略。

2. 示例:支付系统

支付策略接口:

1
2
3
public interface PayStrategy {
void pay(int amount);
}

支付宝策略:

1
2
3
4
5
6
public class AlipayStrategy implements PayStrategy {
@Override
public void pay(int amount) {
System.out.println("支付宝支付:" + amount);
}
}

微信策略:

1
2
3
4
5
6
public class WechatPayStrategy implements PayStrategy {
@Override
public void pay(int amount) {
System.out.println("微信支付:" + amount);
}
}

使用方式:

1
2
PayStrategy strategy = new AlipayStrategy();
strategy.pay(100);

3. 面试延伸

Spring 中大量使用策略模式,例如:

  • HandlerMapping
  • HandlerAdapter
  • ViewResolver

通过接口定义统一行为,不同实现类提供不同策略。

四、观察者模式(Observer)

1. 解决的问题

当一个对象状态变化时,自动通知其他对象。

核心思想:

发布-订阅机制。

2. 示例

观察者接口:

1
2
3
public interface Observer {
void update(String message);
}

具体观察者:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserObserver implements Observer {

private final String name;

public UserObserver(String name) {
this.name = name;
}

@Override
public void update(String message) {
System.out.println(name + " 收到通知:" + message);
}
}

被观察对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.ArrayList;
import java.util.List;

public class NotificationService {

private final List<Observer> observers = new ArrayList<>();

public void addObserver(Observer observer) {
observers.add(observer);
}

public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}

3. 实际应用

典型场景:

  • Spring 事件机制
  • 消息订阅
  • GUI 事件监听
  • MQ 消费者模型

五、代理模式(Proxy)

1. 解决的问题

为对象提供一个代理对象,控制对原对象的访问。

常见用途:

  • 权限控制
  • 日志记录
  • 延迟加载
  • AOP

2. 示例

接口:

1
2
3
public interface Service {
void execute();
}

真实对象:

1
2
3
4
5
6
public class RealService implements Service {
@Override
public void execute() {
System.out.println("执行真实业务");
}
}

代理对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ServiceProxy implements Service {

private final Service realService;

public ServiceProxy(Service realService) {
this.realService = realService;
}

@Override
public void execute() {
System.out.println("权限检查");
realService.execute();
System.out.println("记录日志");
}
}

3. 面试重点

Spring AOP 本质就是代理模式:

  • JDK 动态代理
  • CGLIB 动态代理

六、面试中常见问题

1. Spring 用到了哪些设计模式?

常见答案:

  • 单例模式(Bean 默认单例)
  • 工厂模式(BeanFactory
  • 策略模式(HandlerMapping
  • 代理模式(AOP)
  • 模板方法模式(JdbcTemplate
  • 观察者模式(Spring Event)

2. 单例模式为什么需要 volatile

因为 JVM 可能发生指令重排。

对象创建步骤本应是:

  1. 分配内存
  2. 初始化对象
  3. 指向对象

但可能重排为:

  1. 分配内存
  2. 指向对象
  3. 初始化对象

这样会导致其他线程拿到“未初始化完成”的对象。

volatile 可以禁止这类重排。

3. 策略模式和工厂模式区别

模式 关注点
工厂模式 对象创建
策略模式 算法选择

很多时候这两个模式会结合使用:先由工厂创建策略对象,再在运行时切换策略。

总结

面试中复习设计模式,不需要死记 23 种模式的所有细节。
更重要的是把握这三点:

  • 这个模式解决了什么问题
  • 在项目中哪里能用
  • 在 Spring 或常见框架中有什么对应落地

如果你能结合业务场景讲清楚“为什么用、怎么用、有哪些权衡”,通常会比只背定义更有说服力。