使用策略模式之前的代码
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的依赖注入机制自动完成的
。具体工作原理:
- 策略的收集:
- 由于每个策略类(如
ExamDescriptionUpdateStrategy
、ExamStatusUpdateStrategy
、ExamStartTimeUpdateStrategy
)都标注了@Component
注解 - Spring会自动扫描并实例化这些实现了
UpdateExamStrategy
接口的类 - 所有策略实例会被自动注入到
ExamServiceImpl
中的updateExamStrategies
列表中
- 策略的顺序:
- Spring在注入时会按照以下规则确定策略在列表中的顺序:
- 默认按照类名的字母顺序
- 可以通过
@Order
注解或实现Ordered
接口来自定义顺序
- 策略的使用:
java
@Autowired
public ExamServiceImpl(MongoDBUtils mongoDBUtils, List<UpdateExamStrategy> updateExamStrategies) {
this.mongoDBUtils = mongoDBUtils;
this.updateExamStrategies = updateExamStrategies; // Spring自动注入所有策略
}
- 遍历执行:
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 方法本身更加简洁,并且易于扩展新的更新逻辑。