在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 方法本身更加简洁,并且易于扩展新的更新逻辑。

相关推荐
diudiu96287 分钟前
Logback使用指南
java·开发语言·spring boot·后端·spring·logback
FAFU_kyp22 分钟前
银行技术岗位招聘面试题准备
java·spring boot·spring
Lisonseekpan24 分钟前
Elasticsearch 入门指南
大数据·分布式·后端·elasticsearch·搜索引擎
编程修仙25 分钟前
第十篇 文件上传
spring boot·spring
zhangyifang_00926 分钟前
Spring中的BeanDefinition
java·后端·spring
楠枬36 分钟前
负载均衡 -LoadBalance
后端·spring·spring cloud·负载均衡
milanyangbo36 分钟前
深入解析 Disruptor:从RingBuffer到缓存行填充的底层魔法
java·数据库·后端·架构
计算机学姐1 小时前
基于Python的智能点餐系统【2026最新】
开发语言·vue.js·后端·python·mysql·django·flask
今天你TLE了吗1 小时前
Java:基于注解实现去重表消息防止重复消费
java·spring boot·分布式·spring cloud·幂等
哈哈老师啊1 小时前
Springboot学生选课系统576i3(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端