设计模式是软件开发的"通用套路",它不是凭空创造的语法,而是前辈们总结的、解决特定问题的最优实践。本文结合Java/Spring实战场景,从"是什么、怎么用、怎么选"三个维度,把设计模式讲透,让新手也能快速落地。
一、核心认知:设计模式的三大分类(Java视角)
设计模式本质分三类,对应开发中最核心的三个问题:怎么造对象、怎么拼结构、怎么做交互。以下是高频模式的Java实现和Spring场景映射。
1. 创建型模式:解决"对象怎么造"的问题
这类模式聚焦"对象创建的规则和细节",核心是控制对象的创建方式,避免重复造轮子。
(1)单例模式(Singleton)
核心 :一个类仅创建一个实例,全局共享。
Java实现(懒汉式,线程安全):
java
public class ConfigManager {
// 私有化实例,volatile保证可见性
private static volatile ConfigManager instance;
// 私有化构造器,禁止外部new
private ConfigManager() {}
// 双重检查锁,线程安全且性能高
public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}
// 示例方法:读取配置
public String getConfig(String key) {
return "配置值:" + key;
}
}
Spring场景 :Spring容器中的Bean默认是单例(scope="singleton"),比如@Service、@Controller注解的类,底层就是单例模式的实现。
(2)工厂模式(Factory)
核心 :隐藏对象创建细节,只暴露统一接口。
Java实现(简单工厂):
java
// 产品接口
public interface Payment {
void pay(double amount);
}
// 具体产品:微信支付
public class WechatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount + "元");
}
}
// 具体产品:支付宝支付
public class Alipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount + "元");
}
}
// 工厂类:创建支付对象
public class PaymentFactory {
public static Payment createPayment(String type) {
if ("wechat".equals(type)) {
return new WechatPay();
} else if ("alipay".equals(type)) {
return new Alipay();
}
throw new IllegalArgumentException("不支持的支付方式");
}
}
// 测试
public class TestFactory {
public static void main(String[] args) {
// 调用方只需要指定类型,无需关心创建细节
Payment payment = PaymentFactory.createPayment("wechat");
payment.pay(100); // 输出:微信支付:100.0元
}
}
Spring场景 :Spring的BeanFactory、ApplicationContext就是典型的工厂模式,我们通过getBean()获取对象,无需关心Bean的创建过程。
2. 结构型模式:解决"对象怎么拼"的问题
这类模式聚焦"类/对象的组合方式",核心是让结构更灵活、更兼容。
(1)适配器模式(Adapter)
核心 :让不兼容的接口能一起工作,像"转接头"一样。
Java实现:
java
// 旧系统接口(返回XML)
public interface OldApi {
String getXmlData();
}
// 旧系统实现
public class OldApiImpl implements OldApi {
@Override
public String getXmlData() {
return "<user><name>张三</name></user>";
}
}
// 新系统需要的接口(返回JSON)
public interface NewApi {
String getJsonData();
}
// 适配器:把XML转成JSON
public class ApiAdapter implements NewApi {
private OldApi oldApi;
public ApiAdapter(OldApi oldApi) {
this.oldApi = oldApi;
}
@Override
public String getJsonData() {
// 模拟XML转JSON的逻辑
String xml = oldApi.getXmlData();
return "{\"user\":{\"name\":\"张三\"}}";
}
}
// 测试
public class TestAdapter {
public static void main(String[] args) {
OldApi oldApi = new OldApiImpl();
// 适配器让旧接口适配新系统
NewApi newApi = new ApiAdapter(oldApi);
System.out.println(newApi.getJsonData()); // 输出JSON格式数据
}
}
Spring场景 :SpringMVC的HandlerAdapter就是适配器模式,不同的处理器(Controller)通过适配器统一处理请求。
(2)代理模式(Proxy)
核心 :找个"中间人"控制对象访问,可加前置/后置逻辑。
Java实现(静态代理):
java
// 业务接口
public interface OrderService {
void createOrder(String orderNo);
}
// 真实业务实现
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder(String orderNo) {
System.out.println("创建订单:" + orderNo);
}
}
// 代理类:加日志和权限校验
public class OrderServiceProxy implements OrderService {
private OrderService target;
public OrderServiceProxy(OrderService target) {
this.target = target;
}
@Override
public void createOrder(String orderNo) {
// 前置逻辑:日志+权限校验
System.out.println("日志:开始创建订单");
if (!"admin".equals(getCurrentUser())) {
throw new RuntimeException("无权限创建订单");
}
// 执行真实业务
target.createOrder(orderNo);
// 后置逻辑
System.out.println("日志:订单创建完成");
}
private String getCurrentUser() {
return "admin"; // 模拟获取当前用户
}
}
// 测试
public class TestProxy {
public static void main(String[] args) {
OrderService realService = new OrderServiceImpl();
OrderService proxyService = new OrderServiceProxy(realService);
proxyService.createOrder("ORDER123");
}
}
Spring场景 :Spring AOP的底层就是动态代理模式,比如@Transactional、@Log注解,都是通过代理给方法加增强逻辑。
3. 行为型模式:解决"对象怎么交互"的问题
这类模式聚焦"对象之间的通信和分工",核心是让交互更灵活、解耦。
(1)策略模式(Strategy)
核心 :同一类事情有多种做法,可灵活切换,不改原代码。
Java实现:
java
// 策略接口
public interface SortStrategy {
void sort(int[] arr);
}
// 冒泡排序策略
public class BubbleSort implements SortStrategy {
@Override
public void sort(int[] arr) {
System.out.println("冒泡排序");
// 简单实现冒泡排序
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
// 快速排序策略
public class QuickSort implements SortStrategy {
@Override
public void sort(int[] arr) {
System.out.println("快速排序");
quickSort(arr, 0, arr.length - 1);
}
private void quickSort(int[] arr, int left, int right) {
// 快速排序实现(简化版)
if (left >= right) return;
int pivot = arr[right];
int i = left - 1;
for (int j = left; j < right; j++) {
if (arr[j] <= pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[right];
arr[right] = temp;
quickSort(arr, left, i);
quickSort(arr, i + 2, right);
}
}
// 上下文:使用策略
public class SortContext {
private SortStrategy strategy;
// 动态切换策略
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void sortArray(int[] arr) {
strategy.sort(arr);
}
}
// 测试
public class TestStrategy {
public static void main(String[] args) {
int[] arr = {3,1,4,1,5};
SortContext context = new SortContext();
// 使用冒泡排序
context.setStrategy(new BubbleSort());
context.sortArray(arr);
// 切换为快速排序
context.setStrategy(new QuickSort());
context.sortArray(arr);
}
}
Spring场景 :Spring的Resource接口(不同资源加载策略:文件、网络、类路径)就是策略模式的应用。
(2)观察者模式(Observer)
核心 :一个对象变化,多个对象自动收到通知。
Java实现(基于JDK自带接口):
java
import java.util.Observable;
import java.util.Observer;
// 被观察者:商品库存
public class ProductStock extends Observable {
private int stock;
public void setStock(int stock) {
this.stock = stock;
// 标记状态变化
setChanged();
// 通知所有观察者
notifyObservers(stock);
}
}
// 观察者1:库存告警
public class StockAlarm implements Observer {
@Override
public void update(Observable o, Object arg) {
int stock = (int) arg;
if (stock < 10) {
System.out.println("告警:库存不足,当前库存:" + stock);
}
}
}
// 观察者2:库存日志
public class StockLog implements Observer {
@Override
public void update(Observable o, Object arg) {
int stock = (int) arg;
System.out.println("日志:库存更新为:" + stock);
}
}
// 测试
public class TestObserver {
public static void main(String[] args) {
ProductStock stock = new ProductStock();
// 添加观察者
stock.addObserver(new StockAlarm());
stock.addObserver(new StockLog());
// 修改库存,自动通知观察者
stock.setStock(15); // 仅日志
stock.setStock(8); // 日志+告警
}
}
Spring场景 :Spring的ApplicationEvent和ApplicationListener就是观察者模式,比如ContextRefreshedEvent(容器刷新完成)会通知所有监听器。
二、实战:开发时如何选对设计模式?
选模式的核心是"先定问题类型,再匹配具体场景",不用死记硬背,按以下三步走:
第一步:给问题归类
| 问题类型 | 核心诉求 | 候选模式 |
|---|---|---|
| 创建问题 | 控制对象创建(数量/细节/复杂度) | 单例、工厂、建造者、原型 |
| 结构问题 | 兼容接口/组合对象/简化调用 | 适配器、代理、装饰器、外观 |
| 交互问题 | 对象通信/灵活切换逻辑/分工处理 | 策略、观察者、责任链、模板方法 |
第二步:按场景精准匹配(高频场景)
| 开发场景 | 推荐模式 | 选型理由 |
|---|---|---|
| 全系统唯一实例(配置/日志/连接池) | 单例模式 | 避免重复创建,节省资源 |
| 多类型对象创建(支付方式/数据库连接) | 工厂模式 | 隐藏创建细节,调用方只需指定类型 |
| 新旧接口兼容(旧XML接口对接新JSON系统) | 适配器模式 | 无需修改旧代码,适配新接口 |
| 方法增强(日志/权限/事务) | 代理模式 | 不修改原业务,加前置/后置逻辑 |
| 多算法切换(排序/支付/限流) | 策略模式 | 开闭原则,加新算法不用改原代码 |
| 状态变更通知(库存/订单/消息推送) | 观察者模式 | 解耦通知方和接收方,自动同步状态 |
第三步:避坑(新手必看)
- 拒绝过度设计:简单功能(如普通POJO创建)不用模式,硬套反而增加复杂度;
- 优先选简单模式:小团队优先用工厂、策略、观察者等易理解的模式,避免桥接、备忘录等复杂模式;
- 符合开闭原则:选的模式要支持"扩展开放、修改关闭",比如加新支付方式,只需加新策略类,不用改工厂代码。
三、Spring中的设计模式总结
Spring框架本身就是设计模式的"集大成者",掌握这些对应关系,能更好理解Spring源码:
- 单例模式:Bean默认作用域;
- 工厂模式:
BeanFactory/ApplicationContext; - 代理模式:AOP动态代理(JDK/CGLIB);
- 适配器模式:
HandlerAdapter、MessageConverter; - 策略模式:
Resource、TransactionManager; - 观察者模式:
ApplicationEvent/ApplicationListener。
总结
- 设计模式的核心是解决特定问题的套路,而非语法规则,优先按"创建/结构/交互"三类问题匹配模式;
- Java/Spring开发中,工厂、单例、代理、策略、观察者是5个高频模式,覆盖80%的场景;
- 选模式的关键是"适配场景+避免过度设计",简单功能不用模式,复杂场景优先选团队易理解的模式。
掌握设计模式不是为了"炫技",而是让代码更易维护、更灵活------这也是设计模式的终极目标:写出"优雅且可扩展"的代码。