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

本文是「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 和手机浏览器,完全自主可控。


🔗 项目源码地址


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

  • 点个赞 👍
  • 收藏 ⭐
  • 关注我 👇 获取后续实战文章更新
相关推荐
凤凰战士芭比Q10 小时前
Nexus仓库(maven仓库、Yum仓库、APT仓库)
java·maven
864记忆10 小时前
Linux操作系统自带的测试内存泄漏的命令
java·linux·运维
Jul1en_10 小时前
【算法】分治-归并类题目
java·算法·leetcode·排序算法
tryxr10 小时前
volatile 的作用
java·jvm·volatile·指令重排序
VX:Fegn089510 小时前
计算机毕业设计|基于springboot + vue乡村振兴服务系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
独自归家的兔10 小时前
Java Robot 详解:系统级鼠标 / 键盘模拟的核心原理与实战
java·开发语言
岳轩子10 小时前
DDD领域驱动设计:核心概念、实践结构与框架对比
java·spring
何中应10 小时前
Bean的三种注入方式
开发语言·spring boot·后端·spring
席万里11 小时前
基于Flask框架实现的一个在线考试系统
后端·python·flask
ArabySide11 小时前
【Java】重构之善用多态解耦,记录一次模板方法实践
java·重构·模板方法模式