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

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


🔗 项目源码地址


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

  • 点个赞 👍
  • 收藏 ⭐
  • 关注我 👇 获取后续实战文章更新
相关推荐
無限進步D2 小时前
Java 运行原理
java·开发语言·入门
難釋懷2 小时前
安装Canal
java
是苏浙2 小时前
JDK17新增特性
java·开发语言
不光头强2 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp5 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多5 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood5 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员5 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai