设计模式实战指南(Java 版)
设计模式是 Java 面试必考、代码质量的核心话题。本文不讲 23 种模式全部背诵,而是聚焦生产中最常用的 12 种:单例、工厂、建造者、策略、模板方法、观察者、责任链、代理、适配器、装饰器、状态、门面------每种带实战场景、Java 代码和面试要点。
一、设计模式是什么
1.1 定义
设计模式是前人总结的、针对特定问题的可复用解决方案------不是代码模板,是"解决问题的思路"。
什么时候用:
- 代码开始变乱(if-else 爆炸、重复代码、难扩展);
- 需求经常变("加一种支付方式"这类需求反复出现)。
什么时候别用:
- 简单逻辑硬套模式 = 过度设计;
- 一个 if 能解决的事非要造 5 个类。
核心原则(SOLID 简版):
- 单一职责:一个类只干一件事;
- 开闭原则:对扩展开放,对修改关闭(设计模式的灵魂);
- 依赖倒置:面向接口编程。
二、创建型模式(怎么 new 对象)
2.1 单例模式(面试必考)
场景:全局唯一对象------线程池、配置、连接池。
饿汉式(简单,线程安全):
java
public class ConfigManager {
private static final ConfigManager INSTANCE = new ConfigManager();
private ConfigManager() {}
public static ConfigManager getInstance() { return INSTANCE; }
}
双重检查懒汉式(延迟加载 + 线程安全):
java
public class ConfigManager {
private static volatile ConfigManager instance; // volatile 必须
private ConfigManager() {}
public static ConfigManager getInstance() {
if (instance == null) { // 1. 快速判断
synchronized (ConfigManager.class) {
if (instance == null) { // 2. 加锁后再判断
instance = new ConfigManager();
}
}
}
return instance;
}
}
为什么 volatile :防止 new 指令重排(先赋值后初始化),别的线程拿到半初始化对象。
枚举单例(最简洁,防反射/序列化破坏):
java
public enum ConfigManager {
INSTANCE;
public void load() { ... }
}
面试追问:
- 反射能破坏单例吗?→ 能,枚举除外;
- 序列化会破坏吗?→ 会,重写 readResolve。
2.2 工厂模式
场景:创建对象逻辑复杂、按条件创建不同子类。
简单工厂:
java
public class PaymentFactory {
public static Payment create(String type) {
return switch (type) {
case "alipay" -> new AlipayPayment();
case "wechat" -> new WechatPayment();
default -> throw new IllegalArgumentException("未知支付方式");
};
}
}
问题 :加新支付方式要改工厂------违反开闭。工厂方法模式把创建逻辑下放到子类,但简单场景下简单工厂够用。
实战推荐 :Spring 的 @Bean + 策略注入就是工厂的现代替代(见策略模式)。
2.3 建造者模式
场景:对象字段多、必填可选混合、不可变对象。
java
public class Order {
private final String orderNo; // 必填
private final Long userId; // 必填
private final String couponCode; // 可选
private final String remark; // 可选
private Order(Builder builder) {
this.orderNo = builder.orderNo;
this.userId = builder.userId;
this.couponCode = builder.couponCode;
this.remark = builder.remark;
}
public static class Builder {
private String orderNo;
private Long userId;
private String couponCode;
private String remark;
public Builder(String orderNo, Long userId) { // 必填放构造器
this.orderNo = orderNo;
this.userId = userId;
}
public Builder couponCode(String v) { this.couponCode = v; return this; }
public Builder remark(String v) { this.remark = v; return this; }
public Order build() {
return new Order(this);
}
}
}
// 使用
Order order = new Order.Builder("20260930", 1001L)
.couponCode("DISCOUNT50")
.remark("尽快发货")
.build();
注意 :@Builder(Lombok)就是自动生成建造者------理解原理即可,开发直接用注解。
三、行为型模式(对象怎么协作)
3.1 策略模式(生产使用率最高)
场景:同一行为有多种实现,运行时选择。典型:支付方式、优惠计算、登录方式。
核心:接口 + 多实现 + 选择器。
java
// 1. 策略接口
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
// 2. 多个实现
@Component("alipay")
public class AlipayStrategy implements PaymentStrategy {
public void pay(BigDecimal amount) {
System.out.println("支付宝支付: " + amount);
}
}
@Component("wechat")
public class WechatStrategy implements PaymentStrategy {
public void pay(BigDecimal amount) {
System.out.println("微信支付: " + amount);
}
}
// 3. 选择器(用 Spring 注入所有策略,消除 if-else)
@Component
public class PaymentContext {
private final Map<String, PaymentStrategy> strategies;
// Spring 自动把同接口的 Bean 注入 Map(key = Bean 名)
public PaymentContext(Map<String, PaymentStrategy> strategies) {
this.strategies = strategies;
}
public void pay(String type, BigDecimal amount) {
PaymentStrategy strategy = strategies.get(type);
if (strategy == null) throw new IllegalArgumentException("未知支付方式");
strategy.pay(amount);
}
}
核心价值 :新增支付方式 → 加一个类(@Component),不用改任何现有代码------完美的开闭原则。
面试考点:策略模式和 if-else 的取舍、和状态模式的区别(策略是算法切换,状态是行为随状态变化)。
3.2 模板方法模式
场景:流程骨架固定,步骤实现可变。
java
public abstract class DataImportTemplate {
// 模板方法:定义流程(final 防子类改流程)
public final void importData() {
validate(); // 1. 校验(子类实现)
parse(); // 2. 解析(子类实现)
save(); // 3. 保存(子类实现)
notifyResult(); // 4. 通知(公共实现)
}
protected abstract void validate();
protected abstract void parse();
protected abstract void save();
private void notifyResult() { // 公共逻辑固定
System.out.println("导入完成");
}
}
// 用户导入
public class UserImport extends DataImportTemplate {
protected void validate() { /* 校验用户数据 */ }
protected void parse() { /* 解析 Excel */ }
protected void save() { /* 批量保存 */ }
}
场景 :导入导出、定时任务(Spring 的 ScheduledTask 思路)、消息处理流程。
3.3 观察者模式
场景:一个事件触发多个动作。如订单创建 → 发短信 + 发邮件 + 加积分。
用 Spring 事件(生产标准做法):
java
// 1. 事件
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; }
}
// 2. 发布(业务代码里)
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// ... 业务逻辑
publisher.publishEvent(new OrderCreatedEvent(this, order)); // 发事件
}
}
// 3. 监听者(多个,互不干扰)
@Component
public class SmsListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
sendSms(event.getOrder().getUserPhone());
}
}
@Component
public class PointsListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
addPoints(event.getOrder().getUserId(), 100);
}
}
核心价值 :新增"发优惠券"动作 → 加一个 @EventListener 类,不动 OrderService------解耦。
面试考点 :观察者和发布订阅的区别(观察者是同步直连,发布订阅可通过 MQ 解耦);Spring 事件默认同步,可用 @Async 异步。
3.4 责任链模式
场景:多个处理器依次处理,直到有人处理。典型:审批流、过滤器、校验链。
java
// 1. 处理器抽象
public abstract class OrderHandler {
protected OrderHandler next;
public OrderHandler setNext(OrderHandler next) {
this.next = next;
return next;
}
public final void handle(Order order) {
if (canHandle(order)) {
doHandle(order);
} else if (next != null) {
next.handle(order);
}
}
protected abstract boolean canHandle(Order order);
protected abstract void doHandle(Order order);
}
// 2. 具体处理器
public class VIPHandler extends OrderHandler {
protected boolean canHandle(Order o) { return o.getAmount() > 10000; }
protected void doHandle(Order o) { System.out.println("VIP 订单,人工审批"); }
}
public class NormalHandler extends OrderHandler {
protected boolean canHandle(Order o) { return o.getAmount() <= 10000; }
protected void doHandle(Order o) { System.out.println("普通订单,自动通过"); }
}
// 3. 组链
OrderHandler chain = new VIPHandler()
.setNext(new NormalHandler());
chain.handle(order);
Spring 实现:把多个 Handler 注入 List,按 @Order 排序依次调用------Spring MVC 的拦截器、Spring Security 的 Filter 都是责任链思路。
3.5 状态模式
场景:对象行为随内部状态变化。典型:订单状态机(待支付→已支付→已发货→已完成)。
java
public interface OrderState {
void handle(OrderContext context);
}
public class PendingPaymentState implements OrderState {
public void handle(OrderContext context) {
System.out.println("待支付 → 确认支付");
context.setState(new PaidState());
}
}
public class PaidState implements OrderState {
public void handle(OrderContext context) {
System.out.println("已支付 → 发货");
context.setState(new ShippedState());
}
}
public class OrderContext {
private OrderState state = new PendingPaymentState();
public void setState(OrderState state) { this.state = state; }
public void next() { state.handle(this); }
}
// 使用
OrderContext order = new OrderContext();
order.next(); // 待支付 → 已支付
order.next(); // 已支付 → 已发货
和策略模式区别(面试常问) :状态模式的状态会自动流转 (处理完换状态);策略模式是外部选择算法。
四、结构型模式(怎么组织类)
4.1 代理模式
场景:不直接访问目标,通过代理加控制。典型:AOP、延迟加载、权限控制。
静态代理(一个代理一个目标):
java
public interface UserService { void save(); }
public class UserServiceImpl implements UserService {
public void save() { System.out.println("保存用户"); }
}
// 代理:加日志
public class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) { this.target = target; }
public void save() {
System.out.println("日志: 开始保存");
target.save();
System.out.println("日志: 保存完成");
}
}
动态代理(生产主力) :Spring AOP 就是动态代理------@Transactional、@Async 底层都是它。
java
// JDK 动态代理(目标必须实现接口)
UserService proxy = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
new Class[]{UserService.class},
(p, method, args) -> {
System.out.println("前置逻辑");
Object result = method.invoke(new UserServiceImpl(), args);
System.out.println("后置逻辑");
return result;
}
);
Spring AOP 实战(接口耗时统计):
java
@Aspect
@Component
public class TimeAspect {
@Around("@annotation(com.example.Timed)") // 加 @Timed 的方法自动统计
public Object measure(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long cost = System.currentTimeMillis() - start;
System.out.println(pjp.getSignature() + " 耗时: " + cost + "ms");
}
}
}
面试考点:JDK 代理 vs CGLIB(JDK 要求接口,CGLIB 可代理类;Spring Boot 默认 CGLIB)。
4.2 适配器模式
场景:接口不兼容时转换。典型:第三方 SDK 统一封装、老接口兼容。
java
// 第三方支付接口(不能改)
public class ThirdPartyPayApi {
public String doThirdPay(String params) { return "success"; }
}
// 统一接口
public interface PayGateway {
String pay(String orderNo, BigDecimal amount);
}
// 适配器:把第三方接口包装成统一接口
public class ThirdPartyAdapter implements PayGateway {
private final ThirdPartyPayApi api = new ThirdPartyPayApi();
public String pay(String orderNo, BigDecimal amount) {
String params = buildParams(orderNo, amount); // 转换参数格式
return api.doThirdPay(params);
}
}
和代理模式区别 :代理加控制不改接口,适配器转换接口。
4.3 装饰器模式
场景:动态增强功能,比继承灵活。典型:IO 流(BufferedReader 包装 FileReader)、日志增强。
java
public interface Report { String render(); }
public class BasicReport implements Report {
public String render() { return "基础报表"; }
}
// 装饰器:加 Excel 导出
public class ExcelReportDecorator implements Report {
private final Report report;
public ExcelReportDecorator(Report report) { this.report = report; }
public String render() {
return report.render() + " + Excel 导出";
}
}
// 使用:一层层包
Report report = new ExcelReportDecorator(new PdfReportDecorator(new BasicReport()));
和适配器区别 :适配器转换接口,装饰器增强原接口。
4.4 门面模式(外观模式)
场景:复杂子系统的统一入口。典型:Service 层包装一堆 Mapper。
java
// 下单这个"门面"隐藏了内部一堆组件
@Service
public class OrderFacade {
@Autowired private OrderService orderService;
@Autowired private StockService stockService;
@Autowired private CouponService couponService;
public Order createOrder(OrderDTO dto) {
couponService.useCoupon(dto.getCouponId()); // 用券
stockService.deduct(dto.getSkuId(), dto.getNum()); // 扣库存
return orderService.create(dto); // 建订单
}
}
调用方只依赖 OrderFacade,不用知道内部细节。
五、设计模式在框架中的体现(面试加分)
| 框架/技术 | 用到的模式 |
|---|---|
| Spring IoC | 工厂模式、单例 |
| Spring AOP | 代理模式 |
| Spring 事件 | 观察者模式 |
| MyBatis | 建造者(SqlSessionFactoryBuilder)、代理(Mapper) |
| JDBC DriverManager | 工厂模式 |
| Java IO | 装饰器模式 |
| Tomcat | 责任链(Filter) |
| Spring Security | 责任链(FilterChain) |
| Servlet | 模板方法、门面 |
面试回答技巧:"Spring 的 @Transactional 底层是 AOP,AOP 底层是动态代理,动态代理有 JDK 和 CGLIB 两种实现"------一条线串起来,比背模式定义有用。
六、实战:把 if-else 重构为策略模式
重构前(新需求要改代码):
java
public BigDecimal calcDiscount(String userType, BigDecimal amount) {
if ("vip".equals(userType)) {
return amount.multiply(new BigDecimal("0.9"));
} else if ("svip".equals(userType)) {
return amount.multiply(new BigDecimal("0.8"));
} else if ("black".equals(userType)) {
return amount.multiply(new BigDecimal("0.5"));
} else {
return amount;
}
}
重构后(新需求只要加类):
java
public interface DiscountStrategy {
boolean support(String userType);
BigDecimal calc(BigDecimal amount);
}
@Component
public class VipDiscount implements DiscountStrategy {
public boolean support(String t) { return "vip".equals(t); }
public BigDecimal calc(BigDecimal a) { return a.multiply(new BigDecimal("0.9")); }
}
// ... SVIP、Black 同理
@Component
public class DiscountContext {
private final List<DiscountStrategy> strategies;
public DiscountContext(List<DiscountStrategy> strategies) {
this.strategies = strategies;
}
public BigDecimal calc(String userType, BigDecimal amount) {
return strategies.stream()
.filter(s -> s.support(userType))
.findFirst()
.orElseThrow()
.calc(amount);
}
}
收益:新增"钻石会员" = 新增一个类 + @Component,零改动现有代码。
七、设计模式选择速查
| 需求 | 用哪个 |
|---|---|
| 全局唯一对象 | 单例 |
| 按条件创建对象 | 工厂 |
| 复杂对象构建 | 建造者 |
| 算法可切换 | 策略 |
| 流程固定步骤可变 | 模板方法 |
| 一事件多响应 | 观察者 |
| 多级处理 | 责任链 |
| 状态流转 | 状态 |
| 加控制/日志 | 代理 |
| 接口不兼容 | 适配器 |
| 动态增强 | 装饰器 |
| 统一入口 | 门面 |
本章小结
设计模式的本质是应对变化:策略消除 if-else、观察者解耦事件、责任链组织处理器、代理加横切逻辑。生产中最值钱的是策略、观察者、代理(AOP)、责任链四个------把它们吃透,再配合 Spring 的自动注入,就是标准的企业级写法。
下一篇讲数据结构与算法------大厂面试的硬通货。
(全文完)