借助Spring Boot优雅实现设计模式
作者: shura | 日期: 2025-02-02
说明
本文讲如何利用Spring Boot的依赖注入、自动装配等特性,让设计模式实现更简单优雅。
传统设计模式需要手写工厂注册、对象创建等繁琐代码,用Spring Boot后这些都自动完成。
不是讲Spring源码里的设计模式,是讲我们写业务时怎么用Spring Boot实现设计模式。
介绍5个实用模式:策略、模板方法、责任链、观察者、工厂。
Spring Boot的价值:
- 自动注入所有实现类 → 不用手动注册
- @Order控制顺序 → 不用硬编码
- 事件机制开箱即用 → 不用自己写观察者
适合场景: if-else太多、流程重复、需要扩展性
不适合场景: 简单CRUD、快速开发、小团队
策略模式
问题
支付方式有微信、支付宝、银行卡,传统写法:
@Service
public class PaymentService {
public void pay(String payType, BigDecimal amount) {
if ("WECHAT".equals(payType)) {
// 微信支付逻辑
System.out.println("微信支付: " + amount);
} else if ("ALIPAY".equals(payType)) {
// 支付宝支付逻辑
System.out.println("支付宝支付: " + amount);
} else if ("BANK".equals(payType)) {
// 银行卡支付逻辑
System.out.println("银行卡支付: " + amount);
}
// 新增支付方式需要改这里
}
}
问题:
- 新增支付方式要改if-else
- 代码越来越长
- 违反开闭原则
方案
定义策略接口,每种支付方式一个实现:
// 策略接口
public interface PaymentStrategy {
void pay(BigDecimal amount);
String getType(); // 支付类型
}
// 微信支付
@Component
public class WechatPayment implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("微信支付: " + amount);
// 调用微信API
}
@Override
public String getType() {
return "WECHAT";
}
}
// 支付宝支付
@Component
public class AlipayPayment implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("支付宝支付: " + amount);
// 调用支付宝API
}
@Override
public String getType() {
return "ALIPAY";
}
}
// 银行卡支付
@Component
public class BankPayment implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("银行卡支付: " + amount);
// 调用银行API
}
@Override
public String getType() {
return "BANK";
}
}
策略工厂管理所有策略:
@Component
public class PaymentStrategyFactory {
private final Map<String, PaymentStrategy> strategies;
// Spring自动注入所有PaymentStrategy实现
public PaymentStrategyFactory(List<PaymentStrategy> strategyList) {
this.strategies = strategyList.stream()
.collect(Collectors.toMap(
PaymentStrategy::getType,
strategy -> strategy
));
}
public PaymentStrategy getStrategy(String type) {
PaymentStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new BusinessException("不支持的支付方式: " + type);
}
return strategy;
}
}
使用:
@Service
public class OrderService {
@Autowired
private PaymentStrategyFactory paymentFactory;
public void pay(String payType, BigDecimal amount) {
PaymentStrategy strategy = paymentFactory.getStrategy(payType);
strategy.pay(amount);
}
}
价值
新增支付方式:
- 只需新增一个类实现PaymentStrategy
- 不用改原有代码
- 自动注册到工厂
模板方法模式
问题
导入用户、商品、订单,流程都是:校验 → 转换 → 保存 → 通知
传统写法每个都要写一遍:
public void importUsers(List<UserDTO> users) {
// 1. 校验
for (UserDTO user : users) {
if (user.getName() == null) throw...
}
// 2. 转换
List<User> entities = users.stream()
.map(this::toEntity)
.collect(Collectors.toList());
// 3. 保存
userMapper.batchInsert(entities);
// 4. 通知
eventPublisher.publish("用户导入完成");
}
public void importProducts(List<ProductDTO> products) {
// 又是一套相同流程...
}
问题:
- 流程重复
- 每个都要写一遍
- 难以维护
方案
定义模板骨架,子类实现具体步骤:
// 抽象模板
public abstract class ImportTemplate<T, E> {
// 模板方法 - 定义流程骨架
public void importData(List<T> dataList) {
// 1. 校验
validate(dataList);
// 2. 转换
List<E> entities = convert(dataList);
// 3. 保存
save(entities);
// 4. 通知
notify(entities.size());
}
// 子类实现具体逻辑
protected abstract void validate(List<T> dataList);
protected abstract List<E> convert(List<T> dataList);
protected abstract void save(List<E> entities);
// 钩子方法 - 可选实现
protected void notify(int count) {
System.out.println("导入完成: " + count + " 条");
}
}
用户导入实现:
@Component
public class UserImportService extends ImportTemplate<UserDTO, User> {
@Autowired
private UserMapper userMapper;
@Override
protected void validate(List<UserDTO> dataList) {
for (UserDTO dto : dataList) {
if (dto.getName() == null) {
throw new ValidationException("name", "不能为空");
}
}
}
@Override
protected List<User> convert(List<UserDTO> dataList) {
return dataList.stream()
.map(this::toEntity)
.collect(Collectors.toList());
}
@Override
protected void save(List<User> entities) {
userMapper.batchInsert(entities);
}
private User toEntity(UserDTO dto) {
// DTO -> Entity
return new User(dto.getName(), dto.getAge());
}
}
商品导入实现:
@Component
public class ProductImportService extends ImportTemplate<ProductDTO, Product> {
@Autowired
private ProductMapper productMapper;
@Override
protected void validate(List<ProductDTO> dataList) {
for (ProductDTO dto : dataList) {
if (dto.getPrice().compareTo(BigDecimal.ZERO) <= 0) {
throw new ValidationException("price", "必须大于0");
}
}
}
@Override
protected List<Product> convert(List<ProductDTO> dataList) {
return dataList.stream()
.map(dto -> new Product(dto.getName(), dto.getPrice()))
.collect(Collectors.toList());
}
@Override
protected void save(List<Product> entities) {
productMapper.batchInsert(entities);
}
}
使用:
@Autowired
private UserImportService userImportService;
@Autowired
private ProductImportService productImportService;
// 用户导入
userImportService.importData(userList);
// 商品导入
productImportService.importData(productList);
价值
- 流程固定,只实现差异部分
- 新增导入类型很简单
- 代码复用
责任链模式
问题
下单前有多个校验:库存校验、价格校验、风控校验、优惠券校验
传统写法:
public void createOrder(OrderDTO dto) {
// 校验1: 库存
if (product.getStock() < dto.getQuantity()) {
throw new BusinessException("库存不足");
}
// 校验2: 价格
if (dto.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new ValidationException("amount", "必须大于0");
}
// 校验3: 风控
if (riskService.isRisk(dto.getUserId())) {
throw new BusinessException("风控拦截");
}
// 校验4: 优惠券
if (dto.getCouponId() != null) {
if (!couponService.canUse(dto.getCouponId())) {
throw new BusinessException("优惠券不可用");
}
}
// 创建订单...
}
问题:
- 校验逻辑耦合在一起
- 新增校验要改方法
- 难以动态调整顺序
方案
每个校验一个处理器,组成链条:
// 校验处理器接口
public interface OrderValidator {
void validate(OrderDTO dto);
}
// 库存校验
@Component
@Order(1)
public class StockValidator implements OrderValidator {
@Autowired
private ProductMapper productMapper;
@Override
public void validate(OrderDTO dto) {
Product product = productMapper.selectById(dto.getProductId());
if (product.getStock() < dto.getQuantity()) {
throw new BusinessException("库存不足");
}
}
}
// 价格校验
@Component
@Order(2)
public class PriceValidator implements OrderValidator {
@Override
public void validate(OrderDTO dto) {
if (dto.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new ValidationException("amount", "必须大于0");
}
}
}
// 风控校验
@Component
@Order(3)
public class RiskValidator implements OrderValidator {
@Autowired
private RiskService riskService;
@Override
public void validate(OrderDTO dto) {
if (riskService.isRisk(dto.getUserId())) {
throw new BusinessException("风控拦截");
}
}
}
// 优惠券校验
@Component
@Order(4)
public class CouponValidator implements OrderValidator {
@Autowired
private CouponService couponService;
@Override
public void validate(OrderDTO dto) {
if (dto.getCouponId() != null) {
if (!couponService.canUse(dto.getCouponId())) {
throw new BusinessException("优惠券不可用");
}
}
}
}
责任链管理器:
@Component
public class OrderValidatorChain {
private final List<OrderValidator> validators;
// Spring按@Order顺序注入
public OrderValidatorChain(List<OrderValidator> validators) {
this.validators = validators;
}
public void validate(OrderDTO dto) {
for (OrderValidator validator : validators) {
validator.validate(dto);
}
}
}
使用:
@Service
public class OrderService {
@Autowired
private OrderValidatorChain validatorChain;
public void createOrder(OrderDTO dto) {
// 执行所有校验
validatorChain.validate(dto);
// 创建订单
orderMapper.insert(toEntity(dto));
}
}
价值
- 每个校验独立
- 新增校验只需加一个类
- 用@Order控制顺序
- 可以动态启用/禁用某个校验
观察者模式
问题
用户注册后要做:发邮件、发短信、送积分、记录日志
传统写法:
public void register(UserDTO dto) {
// 1. 保存用户
userMapper.insert(user);
// 2. 发邮件
emailService.sendWelcome(user.getEmail());
// 3. 发短信
smsService.sendWelcome(user.getPhone());
// 4. 送积分
pointService.giveRegisterPoint(user.getId());
// 5. 记录日志
logService.log("用户注册: " + user.getId());
}
问题:
- 注册逻辑耦合了后续操作
- 新增操作要改register方法
- 难以测试
方案
用Spring事件机制解耦:
// 注册事件
public class UserRegisteredEvent {
private Long userId;
private String email;
private String phone;
public UserRegisteredEvent(Long userId, String email, String phone) {
this.userId = userId;
this.email = email;
this.phone = phone;
}
// getters...
}
发布事件:
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void register(UserDTO dto) {
// 1. 保存用户
User user = userMapper.insert(toEntity(dto));
// 2. 发布事件
UserRegisteredEvent event = new UserRegisteredEvent(
user.getId(),
user.getEmail(),
user.getPhone()
);
eventPublisher.publishEvent(event);
}
}
监听器处理后续操作:
// 发邮件
@Component
public class EmailListener {
@Autowired
private EmailService emailService;
@EventListener
@Async // 异步执行
public void handleUserRegistered(UserRegisteredEvent event) {
emailService.sendWelcome(event.getEmail());
}
}
// 发短信
@Component
public class SmsListener {
@Autowired
private SmsService smsService;
@EventListener
@Async
public void handleUserRegistered(UserRegisteredEvent event) {
smsService.sendWelcome(event.getPhone());
}
}
// 送积分
@Component
public class PointListener {
@Autowired
private PointService pointService;
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
pointService.giveRegisterPoint(event.getUserId());
}
}
// 记录日志
@Component
public class LogListener {
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
log.info("用户注册: userId={}", event.getUserId());
}
}
价值
- 注册逻辑只管注册
- 后续操作独立监听
- 新增操作不影响原有代码
- 可以异步执行提升性能
工厂模式
问题
根据订单类型创建不同的订单处理器:
public OrderProcessor getProcessor(String orderType) {
if ("NORMAL".equals(orderType)) {
return new NormalOrderProcessor();
} else if ("PRESALE".equals(orderType)) {
return new PresaleOrderProcessor();
} else if ("GROUPON".equals(orderType)) {
return new GrouponOrderProcessor();
}
throw new BusinessException("不支持的订单类型");
}
问题:
- if-else判断
- new对象没有依赖注入
- 新增类型要改方法
方案
用Spring管理创建:
// 处理器接口
public interface OrderProcessor {
void process(Order order);
String getType();
}
// 普通订单
@Component
public class NormalOrderProcessor implements OrderProcessor {
@Autowired
private OrderService orderService;
@Override
public void process(Order order) {
System.out.println("处理普通订单");
// 可以使用注入的依赖
orderService.createNormalOrder(order);
}
@Override
public String getType() {
return "NORMAL";
}
}
// 预售订单
@Component
public class PresaleOrderProcessor implements OrderProcessor {
@Autowired
private PresaleService presaleService;
@Override
public void process(Order order) {
System.out.println("处理预售订单");
presaleService.createPresaleOrder(order);
}
@Override
public String getType() {
return "PRESALE";
}
}
// 团购订单
@Component
public class GrouponOrderProcessor implements OrderProcessor {
@Autowired
private GrouponService grouponService;
@Override
public void process(Order order) {
System.out.println("处理团购订单");
grouponService.createGrouponOrder(order);
}
@Override
public String getType() {
return "GROUPON";
}
}
工厂:
@Component
public class OrderProcessorFactory {
private final Map<String, OrderProcessor> processors;
public OrderProcessorFactory(List<OrderProcessor> processorList) {
this.processors = processorList.stream()
.collect(Collectors.toMap(
OrderProcessor::getType,
processor -> processor
));
}
public OrderProcessor getProcessor(String type) {
OrderProcessor processor = processors.get(type);
if (processor == null) {
throw new BusinessException("不支持的订单类型: " + type);
}
return processor;
}
}
使用:
@Service
public class OrderService {
@Autowired
private OrderProcessorFactory processorFactory;
public void createOrder(Order order) {
OrderProcessor processor = processorFactory.getProcessor(order.getType());
processor.process(order);
}
}
价值
- 对象由Spring管理
- 自动依赖注入
- 新增类型不改工厂
- 自动注册
对比
策略模式 vs 工厂模式
看起来很像,区别:
工厂模式:
└─ 关注对象创建
└─ 根据类型创建不同对象
策略模式:
└─ 关注算法选择
└─ 根据场景选择不同策略
实际使用中常一起用:工厂创建策略对象。
模板方法 vs 策略模式
模板方法:
└─ 流程固定,步骤可变
└─ 继承实现
策略模式:
└─ 整个算法可变
└─ 组合实现
选择:
- 有固定流程 → 模板方法
- 只是算法不同 → 策略模式
总结
5个实用的设计模式:
| 模式 | 场景 | 价值 |
|---|---|---|
| 策略模式 | 多种支付/物流/折扣方式 | 消除if-else,易扩展 |
| 模板方法 | 流程固定,步骤可变 | 复用流程代码 |
| 责任链 | 多个校验/过滤/处理 | 解耦,动态组合 |
| 观察者 | 一个动作触发多个操作 | 解耦,异步执行 |
| 工厂 | 根据类型创建对象 | Spring管理,自动注入 |
使用建议:
何时使用:
- 有if-else判断类型 → 策略/工厂
- 流程重复 → 模板方法
- 多个校验 → 责任链
- 一个动作多个后续操作 → 观察者
何时不用:
- 只有2-3个分支 → if-else就够了
- 只用一次 → 不要过度设计
- 团队不熟悉 → 先简单实现
原则:
- 先写简单代码
- 出现重复或复杂时再重构
- 不要为了用模式而用模式
再次说明:
设计模式是工具,不是目的。
代码能看懂、能维护,比用什么模式更重要。
简单问题简单解决。
参考:
- 《设计模式: 可复用面向对象软件的基础》
- 《Head First设计模式》
- Spring官方文档 - Events
欢迎关注,学习不迷路!
1万+

被折叠的 条评论
为什么被折叠?



