一、为什么需要策略模式?

在企业项目开发中,经常遇到这样的业务:同一个功能,由于业务规则不同,需要有多种实现方式。

例如支付(支付宝、微信、银联)、文件存储(OSS、MinIO、本地)、消息通知(短信、邮件、钉钉),以及本例的邮件发送(QQ、163、Gmail)。

这些业务有一个共同特点:

业务流程相同,但是具体实现不同。

如果不使用设计模式,通常会写出大量 if-else

public void send(String type) {
    if ("QQ".equals(type)) {
        // QQ邮箱发送逻辑
    } else if ("163".equals(type)) {
        // 163邮箱发送逻辑
    } else if ("GMAIL".equals(type)) {
        // Gmail发送逻辑
    }
}

随着邮箱类型不断增多,这个方法会越来越庞大,每次新增类型都要修改原有代码,明显违反了开闭原则(对扩展开放,对修改关闭)。这时候就可以使用策略模式。


二、策略模式是什么?

策略模式(Strategy Pattern):

定义一组算法或业务规则,将每种实现分别封装起来,并且可以在运行时动态替换。

以前是一个类里面写所有 if-else 逻辑,使用策略模式后,每个策略负责自己的业务,通过统一接口对外暴露。


三、项目中的策略接口

本项目的 EmailStrategy 就是策略抽象,定义了两个方法:发送邮件和获取邮箱类型。

// EmailStrategy.java
package com.example.email.strategy;

import com.example.email.dto.MailRequest;

public interface EmailStrategy {

    void send(MailRequest request);

    String getType();

}

该接口不依赖于任何具体的邮箱实现,只约定行为。


四、不同邮箱实现不同策略

QQ邮箱策略

// QQEmailStrategy.java
package com.example.email.strategy;

import com.example.email.dto.MailRequest;
import org.springframework.stereotype.Component;

@Component
public class QQEmailStrategy implements EmailStrategy {

    @Override
    public void send(MailRequest request) {
        System.out.println("QQ邮箱发送邮件...");
        System.out.println("收件人:" + request.getTo());
    }

    @Override
    public String getType() {
        return "QQ";
    }

}

163邮箱策略

// Email163Strategy.java
package com.example.email.strategy;

import com.example.email.dto.MailRequest;
import org.springframework.stereotype.Component;

@Component
public class Email163Strategy implements EmailStrategy {

    @Override
    public void send(MailRequest request) {
        System.out.println("163邮箱发送邮件...");
    }

    @Override
    public String getType() {
        return "163";
    }

}

Gmail策略

// GmailStrategy.java
package com.example.email.strategy;

import com.example.email.dto.MailRequest;
import org.springframework.stereotype.Component;

@Component
public class GmailStrategy implements EmailStrategy {

    @Override
    public void send(MailRequest request) {
        System.out.println("Gmail发送邮件...");
    }

    @Override
    public String getType() {
        return "GMAIL";
    }

}

每个策略都是一个独立的 @Component,由 Spring 统一管理。新增邮箱时只需新增一个 EmailStrategy 实现类即可,无需修改任何已有代码。


五、工厂模式:如何选择具体策略

策略接口解决了"每种邮箱如何发送"的问题,但业务调用 service.send(request) 时,系统怎么知道选择哪个策略?

通常配合工厂模式解决。

// EmailStrategyFactory.java
package com.example.email.factory;

import com.example.email.strategy.EmailStrategy;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class EmailStrategyFactory {

    private final Map<String, EmailStrategy> strategyMap;

    public EmailStrategyFactory(List<EmailStrategy> strategies) {
        this.strategyMap = strategies.stream()
                .collect(Collectors.toMap(
                        EmailStrategy::getType,
                        Function.identity()
                ));
    }

    public EmailStrategy getStrategy(String type) {
        EmailStrategy strategy = strategyMap.get(type);
        if (strategy == null) {
            throw new RuntimeException("不支持的邮箱类型:" + type);
        }
        return strategy;
    }

}

核心原理:Spring 启动时,会自动将所有 EmailStrategy 实现类注入到 List<EmailStrategy> 中,工厂将其转换为 Map<type, strategy> 结构。

调用时只需:

EmailStrategy strategy = factory.getStrategy("QQ");
strategy.send(request);

业务代码完全不知道具体实现类,只通过接口交互。


六、模板模式:固定发送流程

邮件发送的完整流程是固定的,但具体发送行为是变化的。固定部分适合用模板模式封装。

// AbstractEmailTemplate.java
package com.example.email.service;

import com.example.email.dto.MailRequest;

public abstract class AbstractEmailTemplate {

    public final void send(MailRequest request) {
        validate(request);
        beforeSend(request);
        doSend(request);
        afterSend(request);
    }

    protected void validate(MailRequest request) {
        if (request.getTo() == null || request.getTo().isEmpty()) {
            throw new RuntimeException("收件人不能为空");
        }
    }

    protected void beforeSend(MailRequest request) {
        System.out.println("开始发送邮件");
    }

    protected abstract void doSend(MailRequest request);

    protected void afterSend(MailRequest request) {
        System.out.println("邮件发送结束");
    }

}

send() 方法用 final 修饰,确保子类不能修改流程顺序。doSend() 是抽象方法,由子类实现。


七、服务层:模板 + 策略 + 工厂组合

// EmailSendService.java
package com.example.email.service;

import com.example.email.dto.MailRequest;
import com.example.email.factory.EmailStrategyFactory;
import com.example.email.strategy.EmailStrategy;
import org.springframework.stereotype.Service;

@Service
public class EmailSendService extends AbstractEmailTemplate {

    private final EmailStrategyFactory factory;

    public EmailSendService(EmailStrategyFactory factory) {
        this.factory = factory;
    }

    @Override
    protected void doSend(MailRequest request) {
        EmailStrategy strategy = factory.getStrategy(request.getEmailType());
        strategy.send(request);
    }

}

EmailSendService 继承模板,实现 doSend() —— 内部通过工厂获取对应策略执行发送。三者协作关系如下:

Controller
    |
    v
EmailSendService.send(request)        ← 模板模式(固定流程)
    |
    +--- validate(request)            ← 参数校验
    +--- beforeSend(request)          ← 前置日志
    +--- doSend(request)              ← 抽象方法,由子类实现
    |       |
    |       v
    |   EmailStrategyFactory          ← 工厂模式(策略选择)
    |       |
    |       v
    |   QQEmailStrategy / Email163Strategy / GmailStrategy
    |                                   ← 策略模式(具体发送)
    +--- afterSend(request)           ← 后置处理

八、AOP 日志增强

在 Spring Boot 中还可以通过 AOP 统一增强日志,而无需改动业务代码。

// EmailLogAspect.java
package com.example.email.aspect;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class EmailLogAspect {

    @Before("execution(* com.example.email.service.EmailSendService.send(..))")
    public void before() {
        System.out.println("记录邮件发送日志");
    }

    @AfterReturning("execution(* com.example.email.service.EmailSendService.send(..))")
    public void after() {
        System.out.println("邮件发送成功日志");
    }

}

@Before 在方法执行前记录日志,@AfterReturning 在方法成功返回后记录日志。这种横切关注点与业务完全解耦。


九、Controller 层

// EmailController.java
package com.example.email.controller;

import com.example.email.dto.MailRequest;
import com.example.email.service.EmailSendService;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/email")
public class EmailController {

    private final EmailSendService service;

    public EmailController(EmailSendService service) {
        this.service = service;
    }

    @PostMapping("/send")
    public String send(@RequestBody MailRequest request) {
        service.send(request);
        return "success";
    }

}

请求体示例:

{
    "emailType": "QQ",
    "to": "test@qq.com",
    "subject": "测试邮件",
    "content": "hello"
}

十、项目完整结构

email-demo
├── pom.xml
└── src/main/java/com/example/email/
    ├── EmailApplication.java           # Spring Boot 启动类
    ├── controller/EmailController.java # REST 接口入口
    ├── dto/MailRequest.java            # 请求参数 DTO
    ├── strategy/
    │   ├── EmailStrategy.java          # 策略接口(抽象)
    │   ├── QQEmailStrategy.java        # QQ 策略(具体实现)
    │   ├── Email163Strategy.java       # 163 策略
    │   └── GmailStrategy.java          # Gmail 策略
    ├── factory/
    │   └── EmailStrategyFactory.java   # 策略工厂
    ├── service/
    │   ├── AbstractEmailTemplate.java  # 模板模式(固定流程)
    │   └── EmailSendService.java       # 服务(模板+工厂+策略)
    └── aspect/
        └── EmailLogAspect.java         # AOP 日志切面

十一、策略模式优点总结

优点 说明
消除 if-else factory.getStrategy(type) 替代了多重条件判断
开闭原则 新增邮箱只需新增策略类,无需修改核心业务
方便测试 每个策略独立,可单独测试
组合灵活 可配合模板模式、工厂模式、单例模式、AOP 组合使用

十二、适用场景

当业务中出现同一个功能,根据不同条件执行不同逻辑时,就可以考虑策略模式。常见场景包括但不限于:

  • 支付渠道:支付宝、微信、银联
  • 文件存储:OSS、MinIO、本地
  • IoT 协议:MQTT、Modbus、OPC UA
  • 消息通知:短信、邮件、微信、钉钉
  • 第三方 SDK:海康、大华、腾讯云、阿里云

十三、总结

策略模式的核心思想是把变化的业务逻辑封装成不同策略,通过统一接口调用,让系统具备更好的扩展能力

在 Spring Boot 企业项目中,策略模式通常与工厂模式(策略选择)、模板模式(流程固定)、单例模式(对象复用)、AOP(横切增强)组合使用,共同构建出高内聚低耦合的架构。邮件发送只是一个入门案例,但其设计思想可以复用到支付、存储、通信、通知等几乎所有业务场景中。

posted on 2026-07-30 21:04  认真的派大星  阅读(0)  评论(0)    收藏  举报