借助SpringBoot优雅实现设计模式

借助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就够了
  • 只用一次 → 不要过度设计
  • 团队不熟悉 → 先简单实现

原则:

  1. 先写简单代码
  2. 出现重复或复杂时再重构
  3. 不要为了用模式而用模式

再次说明:

设计模式是工具,不是目的。

代码能看懂、能维护,比用什么模式更重要。

简单问题简单解决。


参考:

  • 《设计模式: 可复用面向对象软件的基础》
  • 《Head First设计模式》
  • Spring官方文档 - Events

欢迎关注,学习不迷路!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值