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

相关推荐
AdSet聚合广告2 小时前
APP广告变现,开发者如何判断对接的广告SDK安全合规?
大数据·后端·算法·安全·uni-app
野犬寒鸦4 小时前
Redis核心数据结构操作指南:字符串、哈希、列表详解
数据结构·数据库·redis·后端·缓存·哈希算法
无敌小肥0074 小时前
Springboot 整合 WebSocket 实现聊天室功能
spring boot·后端·websocket
代码老y5 小时前
基于springboot的校园商铺管理系统的设计与实现
java·vue.js·spring boot·后端·毕业设计·课程设计·个人开发
胡尔摩斯.6 小时前
ElasticSearch--DSL查询语句
后端·python·elasticsearch·django
曼岛_7 小时前
[Java实战]Spring Boot切面编程实现日志记录(三十六)
java·开发语言·spring boot
朱颜辞镜花辞树‎7 小时前
读《Go语言圣经》记录(一)
开发语言·后端·golang
why1517 小时前
贝壳后端golang面经
开发语言·后端·golang
Channing Lewis8 小时前
go中的channel
开发语言·后端·golang