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

相关推荐
线条11 小时前
SpringBoot 应用开发核心分层架构与实战详解
spring boot·后端·架构
是紫焅呢3 小时前
E结构体基础.go
开发语言·后端·golang·学习方法·visual studio code
clt1233213 小时前
golang excel导出时需要显示刷新
开发语言·后端·golang
Silverdew*3 小时前
vs code配置go开发环境以及问题解决 could not import cannot find package in GOROOT or GOPATH
开发语言·后端·golang
执 、4 小时前
SpringBoot定时监控数据库状态
java·数据库·ide·spring boot·后端
武子康7 小时前
Java-49 深入浅出 Tomcat 手写 Tomcat 实现【02】HttpServlet Request RequestProcessor
java·开发语言·后端·学习·spring cloud·tomcat
狮子也疯狂8 小时前
基于Spring Boot的宿舍管理系统设计与实现
java·spring boot·后端
PetterHillWater8 小时前
研发技术之路回忆录之一
后端
程序员一诺python9 小时前
【Django开发】django美多商城项目完整开发4.0第2篇:项目准备,配置【附代码文档】
后端·python·django·框架
GettingReal11 小时前
Python 构建壳来启动加密的 SpringBoot Jar 包,增加反编译难度
spring boot·python·jar·加密