项目中如何用策略模式实现多角色登录解耦?(附实战代码)

本文是「EduCore 教务系统实战系列」第 4 篇,将通过实际代码案例,详解我如何使用策略模式优化多角色登录认证流程,实现登录逻辑的解耦与扩展。


🎯 背景:多角色登录如何设计更优雅?

在 EduCore 教务系统中,存在以下三种角色需要登录:

  • ADMIN(管理员)
  • TEACHER(教师)
  • STUDENT(学生)

如果不使用任何设计模式,登录接口代码大概率会是这样的:

java 复制代码
@PostMapping("/login")
public R login(@RequestBody Account account) {
    Account dbAccount = null;

    if ("ADMIN".equals(account.getRole())) {
        dbAccount = adminService.login(account);
    } else if ("TEACHER".equals(account.getRole())) {
        dbAccount = teacherService.login(account);
    } else if ("STUDENT".equals(account.getRole())) {
        dbAccount = studentService.login(account);
    }

    if (dbAccount == null) return R.error("账号或密码错误");

    String token = jwtUtils.createToken(dbAccount);
    return R.ok().put("token", token).put("user", dbAccount);
}

你可以看到这段代码的问题:

  • 角色判断耦合严重,一旦新增角色,必须修改这里;
  • 服务逻辑难以扩展,不同角色有不同校验规则或来源;
  • 不符合开闭原则(OCP),代码不具备良好扩展性;
  • 测试与调试困难,逻辑集中耦合,不利于单元测试;

✅ 引入策略模式解决耦合问题

策略模式是一种行为型设计模式,它允许在运行时选择算法或行为。

我们可以为每种角色登录逻辑抽象成一个策略类,接口如下:

java 复制代码
public interface LoginStrategy {
    Account login(Account account);
}

🧱 代码实现步骤

Step 1️⃣ 定义统一策略接口

java 复制代码
public interface LoginStrategy {
    Account login(Account account);
}

Step 2️⃣ 各角色实现策略逻辑

java 复制代码
@Component("ADMIN")
public class AdminLoginStrategy implements LoginStrategy {
    @Autowired private AdminService adminService;
    @Override
    public Account login(Account account) {
        return adminService.login(account);
    }
}

@Component("TEACHER")
public class TeacherLoginStrategy implements LoginStrategy {
    @Autowired private TeacherService teacherService;
    @Override
    public Account login(Account account) {
        return teacherService.login(account);
    }
}

@Component("STUDENT")
public class StudentLoginStrategy implements LoginStrategy {
    @Autowired private StudentService studentService;
    @Override
    public Account login(Account account) {
        return studentService.login(account);
    }
}

💡 说明:

  • 每个策略只关心自己角色的登录逻辑;
  • 各服务如 adminService.login() 可做密码加密校验、账号冻结判断等扩展逻辑;

Step 3️⃣ 创建策略工厂类

java 复制代码
@Component
public class LoginStrategyFactory {

    @Autowired
    private Map<String, LoginStrategy> strategyMap;

    public LoginStrategy getStrategy(String role) {
        LoginStrategy strategy = strategyMap.get(role);
        if (strategy == null) {
            throw new IllegalArgumentException("Unsupported role: " + role);
        }
        return strategy;
    }
}

🧠 Spring 会自动将 @Component("ADMIN") 等注入为 Map,key 为 Bean 名称,value 为实现类。


Step 4️⃣ 控制器使用工厂调度策略

java 复制代码
@RestController
@RequestMapping("/auth")
public class LoginController {

    @Autowired
    private LoginStrategyFactory loginStrategyFactory;

    @Autowired
    private JwtUtils jwtUtils;

    @PostMapping("/login")
    public R login(@RequestBody Account account) {
        LoginStrategy strategy = loginStrategyFactory.getStrategy(account.getRole());
        Account dbAccount = strategy.login(account);
        if (dbAccount == null) return R.error("账号或密码错误");

        String token = jwtUtils.createToken(dbAccount);
        return R.ok().put("token", token).put("user", dbAccount);
    }
}

🌱 效果对比总结

对比维度 原始 if-else 实现 策略模式实现
可读性 低(if 嵌套复杂) 高(逻辑分离)
扩展性 差,新增角色要改 controller 强,新增策略类即可
测试性 较差 容易单测每个策略
开闭原则 不符合 完全符合

🧠 常见问题解答

❓ 策略模式是否会影响性能?

不会。Spring 的依赖注入在启动阶段完成,调用时只涉及 Map 查找与方法调用,性能开销可以忽略。

❓ 角色名作为 Bean 名是否不太优雅?

可以封装为 Enum 并加校验;或用 @Qualifier 指定 Bean 名来避免硬编码。


📦 项目结构片段

plaintext 复制代码
├── strategy
│   ├── LoginStrategy.java
│   ├── AdminLoginStrategy.java
│   ├── TeacherLoginStrategy.java
│   └── StudentLoginStrategy.java
├── factory
│   └── LoginStrategyFactory.java
├── controller
│   └── LoginController.java

🔚 总结

通过引入策略模式,我实现了:

  • ✅ 多角色登录逻辑完全解耦;
  • ✅ 角色扩展无需修改核心控制器;
  • ✅ 每个策略可单独调试、测试;
  • ✅ 构建了一套更优雅、更工程化的认证体系;

这种设计也为后续引入责任链模式、模板方法、缓存优化等打下良好基础。


📌 下一篇预告

📌《无微信依赖!纯网页扫码登录实现方案详解》

你将看到一个不依赖微信或 App 的纯网页扫码登录功能,适配 PC 和手机浏览器,完全自主可控。


🔗 项目源码地址


🙋‍♂️ 如果你觉得这篇文章有帮助:

  • 点个赞 👍
  • 收藏 ⭐
  • 关注我 👇 获取后续实战文章更新
相关推荐
Jm_洋洋4 分钟前
【C++进阶】虚函数、虚表与虚指针:多态底层机制剖析
java·开发语言·c++
小马爱打代码6 分钟前
MyBatis:缓存体系设计与避坑大全
java·缓存·mybatis
时艰.12 分钟前
Java 并发编程:Callable、Future 与 CompletableFuture
java·网络
码云数智-园园13 分钟前
深入理解与正确实现 .NET 中的 BackgroundService
java·开发语言
好好研究16 分钟前
SpringBoot整合SpringMVC
xml·java·spring boot·后端·mvc
千寻技术帮17 分钟前
10386_基于SpringBoot的外卖点餐管理系统
java·spring boot·vue·外卖点餐
曹轲恒19 分钟前
SpringBoot整合SpringMVC(末)
java·spring boot·后端
_周游19 分钟前
Java8 API 文档搜索引擎_2.索引模块(程序)
java·搜索引擎·intellij-idea
小马爱打代码21 分钟前
Spring Boot:邮件发送生产可落地方案
java·spring boot·后端
BD_Marathon27 分钟前
设计模式——接口隔离原则
java·设计模式·接口隔离原则