在SpringBoot项目中策略模式的使用

使用策略模式之前的代码

java 复制代码
 @Override
    public void updateExam(String id, ExamUpdateDTO examUpdateDTO) {
        logger.info("Service: 修改考试场次, ID: {}, 数据: {}", id, examUpdateDTO);

        Exam existingExam = mongoDBUtils.findById(id, Exam.class);
        if (existingExam == null) {
            throw new EntityNotFoundException("考试场次不存在: " + id);
        }

        Map<String, Object> examUpdateMap = new HashMap<>();
        if (examUpdateDTO.getExamName() != null) {
            examUpdateMap.put("examName", examUpdateDTO.getExamName());
        }
        if (examUpdateDTO.getExamDescription() != null) {
            examUpdateMap.put("examDescription", examUpdateDTO.getExamDescription());
        }
        if (examUpdateDTO.getStartTime() != null) {
            examUpdateMap.put("startTime", examUpdateDTO.getStartTime());
        }
        if (examUpdateDTO.getDuration() != null) {
            examUpdateMap.put("duration", examUpdateDTO.getDuration());
        }
        if (examUpdateDTO.getStatus() != null) {
            examUpdateMap.put("status", examUpdateDTO.getStatus());
        }

        if (examUpdateMap.isEmpty()) {
            logger.warn("Service: 没有可更新的考试场次字段, ID: {}", id);
            return;
        }
        
        examUpdateMap.put("updateTime", System.currentTimeMillis());

        boolean examUpdated = mongoDBUtils.updateOne(Criteria.where("ID").is(id), examUpdateMap, Exam.class);

        if (!examUpdated) {
            logger.warn("Service: 更新考试场次失败 or no changes made, ID: {}", id);
        }
        logger.info("Service: 考试场次基础信息更新已提交, ID: {}", id);

        boolean needsStudentUpdate = examUpdateDTO.getStartTime() != null || examUpdateDTO.getDuration() != null;

        if (needsStudentUpdate) {
            Map<String, Object> studentUpdateMap = new HashMap<>();
            if (examUpdateDTO.getStartTime() != null) {
                studentUpdateMap.put("startTime", examUpdateDTO.getStartTime());
            }
            if (examUpdateDTO.getDuration() != null) {
                studentUpdateMap.put("duration", examUpdateDTO.getDuration());
            }

            if (!studentUpdateMap.isEmpty()) {
                Criteria studentCriteria = Criteria.where("examID").is(id);
                boolean studentsUpdated = mongoDBUtils.updateMany(studentCriteria, studentUpdateMap, CompetitionSignUpLog.class);
                if (studentsUpdated) {
                    logger.info("Service: 成功级联更新考试场次 ID {} 下学员的 startTime/duration.", id);
                } else {
                    logger.warn("Service: 级联更新考试场次 ID {} 下学员的 startTime/duration 操作未被确认或无匹配学员.", id);
                }
            }
        }
    }

创建策略接口即策略实现类

java 复制代码
/**
 * 更新考试场次信息的策略接口
 */
public interface UpdateExamStrategy {
    /**
     * 根据DTO将待更新的字段填充到相应的map中。
     *
     * @param existingExam   当前的考试实体 (可用于比较或特定逻辑)
     * @param dto            考试更新DTO
     * @param examUpdateMap  用于收集考试实体自身更新的字段
     * @param studentUpdateMap 用于收集需要级联更新到学员报名记录的字段
     */
    void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap);
} 
java 复制代码
@Component
public class ExamDescriptionUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getExamDescription() != null) {
            examUpdateMap.put("examDescription", dto.getExamDescription());
        }
    }
}
java 复制代码
@Component
public class ExamDurationUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getDuration() != null) {
            examUpdateMap.put("duration", dto.getDuration());
            studentUpdateMap.put("duration", dto.getDuration()); // Also update for students
        }
    }
} ```

```java
@Component
public class ExamNameUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getExamName() != null) {
            examUpdateMap.put("examName", dto.getExamName());
        }
    }
} 
java 复制代码
@Component
public class ExamStartTimeUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getStartTime() != null) {
            examUpdateMap.put("startTime", dto.getStartTime());
            studentUpdateMap.put("startTime", dto.getStartTime()); // Also update for students
        }
    }
} 
java 复制代码
@Component
public class ExamStatusUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getStatus() != null) {
            examUpdateMap.put("status", dto.getStatus());
        }
    }
}

使用策略模式之后的updateExam方法

java 复制代码
@Service
public class ExamServiceImpl implements ExamService {

    private static final Logger logger = LogManager.getLogger(ExamServiceImpl.class);

    @Autowired
    private MongoDBUtils mongoDBUtils;

    private final List<UpdateExamStrategy> updateExamStrategies;

    @Autowired
    public ExamServiceImpl(MongoDBUtils mongoDBUtils, List<UpdateExamStrategy> updateExamStrategies) {
        this.mongoDBUtils = mongoDBUtils; // Ensure mongoDBUtils is assigned if it's not already via @Autowired field injection
        this.updateExamStrategies = updateExamStrategies;
    }
    //...省略其他方法的实现
    @Override
    public void updateExam(String id, ExamUpdateDTO examUpdateDTO) {
        logger.info("Service: 修改考试场次, ID: {}, 数据: {}", id, examUpdateDTO);

        Exam existingExam = mongoDBUtils.findById(id, Exam.class);
        if (existingExam == null) {
            throw new EntityNotFoundException("考试场次不存在: " + id);
        }

        Map<String, Object> examUpdateMap = new HashMap<>();
        Map<String, Object> studentUpdateMap = new HashMap<>();

        // 遍历所有策略并应用更新
        for (UpdateExamStrategy strategy : updateExamStrategies) {
            strategy.applyUpdate(existingExam, examUpdateDTO, examUpdateMap, studentUpdateMap);
        }

        if (!examUpdateMap.isEmpty()) {
            examUpdateMap.put("updateTime", System.currentTimeMillis());
            boolean examUpdated = mongoDBUtils.updateOne(Criteria.where("ID").is(id), examUpdateMap, Exam.class);
            if (!examUpdated) {
                logger.warn("Service: 更新考试场次失败 or no changes made, ID: {}", id);
            } else {
                logger.info("Service: 考试场次基础信息更新已提交, ID: {}", id);
            }
        } else {
            logger.info("Service: 没有可更新的考试场次字段, ID: {}", id);
            // 如果 examUpdateMap 为空,但 studentUpdateMap 不为空,则不需要返回
            // 即使考试本身没有变化,关联的学生信息可能仍需更新
        }

        if (!studentUpdateMap.isEmpty()) {
            Criteria studentCriteria = Criteria.where("examID").is(id);
            boolean studentsUpdated = mongoDBUtils.updateMany(studentCriteria, studentUpdateMap, CompetitionSignUpLog.class);
            if (studentsUpdated) {
                logger.info("Service: 成功级联更新考试场次 ID {} 下学员的相关信息.", id);
            } else {
                logger.warn("Service: 级联更新考试场次 ID {} 下学员信息的操作未被确认或无匹配学员.", id);
            }
        }
    }
 }   

在这个场景中,策略的选择是由Spring的依赖注入机制自动完成的。具体工作原理:

  1. 策略的收集
  • 由于每个策略类(如ExamDescriptionUpdateStrategyExamStatusUpdateStrategyExamStartTimeUpdateStrategy)都标注了@Component注解
  • Spring会自动扫描并实例化这些实现了UpdateExamStrategy接口的类
  • 所有策略实例会被自动注入到ExamServiceImpl中的updateExamStrategies列表中
  1. 策略的顺序
  • Spring在注入时会按照以下规则确定策略在列表中的顺序:
    • 默认按照类名的字母顺序
    • 可以通过@Order注解或实现Ordered接口来自定义顺序
  1. 策略的使用
java 复制代码
@Autowired
public ExamServiceImpl(MongoDBUtils mongoDBUtils, List<UpdateExamStrategy> updateExamStrategies) {
    this.mongoDBUtils = mongoDBUtils;
    this.updateExamStrategies = updateExamStrategies;  // Spring自动注入所有策略
}
  1. 遍历执行
java 复制代码
for (UpdateExamStrategy strategy : updateExamStrategies) {
    strategy.applyUpdate(existingExam, examUpdateDTO, examUpdateMap, studentUpdateMap);
}
  • 每次遍历会取出列表中的一个策略实例
  • 每个策略实例都是一个具体的策略类的对象(如ExamDescriptionUpdateStrategy的实例)
  • 调用策略的applyUpdate方法时,会执行对应策略类中的具体实现

这是一个典型的策略模式结合Spring依赖注入的应用,它的好处是:

  • 解耦:各个更新策略相互独立
  • 可扩展:添加新的更新策略只需创建新的策略类并添加@Component注解
  • 维护性好:每个策略类负责处理特定的更新逻辑

总结

这里添加了一个构造函数来注入 mongoDBUtils 和

updateExamStrategies。如果你的项目中 mongoDBUtils 已经通过字段上的 @Autowired

注入,那么在构造函数中再次赋值是多余的,但通常无害。Spring 的依赖注入会处理好这些。

主要逻辑已更改为: 初始化 examUpdateMap 和 studentUpdateMap。 遍历所有注入的 UpdateExamStrategy 策略。 每个策略根据 ExamUpdateDTO 的内容,决定是否要向 examUpdateMap(用于更新 Exam 实体本身)或

studentUpdateMap(用于级联更新 CompetitionSignUpLog 实体)添加键值对。 如果

examUpdateMap 不为空,则更新 Exam 实体。 如果 studentUpdateMap 不为空,则更新相关的CompetitionSignUpLog 实体。 这种方式将每个字段的更新逻辑(包括是否需要级联更新)封装在各自的策略类中,使得 updateExam 方法本身更加简洁,并且易于扩展新的更新逻辑。

相关推荐
Livingbody20 分钟前
ubuntu25.04完美安装typora免费版教程
后端
阿华的代码王国28 分钟前
【Android】RecyclerView实现新闻列表布局(1)适配器使用相关问题
android·xml·java·前端·后端
码农BookSea32 分钟前
自研 DSL 神器:万字拆解 ANTLR 4 核心原理与高级应用
java·后端
lovebugs34 分钟前
Java并发编程:深入理解volatile与指令重排
java·后端·面试
海奥华21 小时前
操作系统到 Go 运行时的内存管理演进与实现
开发语言·后端·golang
codervibe1 小时前
Spring Boot 服务层泛型抽象与代码复用实战
后端
_風箏1 小时前
Shell【脚本 04】传递参数的4种方式(位置参数、特殊变量、环境变量和命名参数)实例说明
后端
斜月1 小时前
Python Asyncio以及Futures并发编程实践
后端·python
CRUD被占用了1 小时前
coze-studio学习笔记(一)
后端