在SpringBoot項目中策略模式的使用方式
使用策略模式之前的代碼
@Override
public void updateExam(String id, ExamUpdateDTO examUpdateDTO) {
logger.info("Service: 修改考試場次, ID: {}, 數(shù)據(jù): {}", 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: 考試場次基礎(chǔ)信息更新已提交, 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: 成功級聯(lián)更新考試場次 ID {} 下學員的 startTime/duration.", id);
} else {
logger.warn("Service: 級聯(lián)更新考試場次 ID {} 下學員的 startTime/duration 操作未被確認或無匹配學員.", id);
}
}
}
}
創(chuàng)建策略接口即策略實現(xiàn)類
/**
* 更新考試場次信息的策略接口
*/
public interface UpdateExamStrategy {
/**
* 根據(jù)DTO將待更新的字段填充到相應(yīng)的map中。
*
* @param existingExam 當前的考試實體 (可用于比較或特定邏輯)
* @param dto 考試更新DTO
* @param examUpdateMap 用于收集考試實體自身更新的字段
* @param studentUpdateMap 用于收集需要級聯(lián)更新到學員報名記錄的字段
*/
void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap);
}
@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());
}
}
}
@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());
}
}
}
@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
}
}
}
@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方法
@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;
}
//...省略其他方法的實現(xiàn)
@Override
public void updateExam(String id, ExamUpdateDTO examUpdateDTO) {
logger.info("Service: 修改考試場次, ID: {}, 數(shù)據(jù): {}", 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<>();
// 遍歷所有策略并應(yīng)用更新
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: 考試場次基礎(chǔ)信息更新已提交, ID: {}", id);
}
} else {
logger.info("Service: 沒有可更新的考試場次字段, ID: {}", id);
// 如果 examUpdateMap 為空,但 studentUpdateMap 不為空,則不需要返回
// 即使考試本身沒有變化,關(guān)聯(lián)的學生信息可能仍需更新
}
if (!studentUpdateMap.isEmpty()) {
Criteria studentCriteria = Criteria.where("examID").is(id);
boolean studentsUpdated = mongoDBUtils.updateMany(studentCriteria, studentUpdateMap, CompetitionSignUpLog.class);
if (studentsUpdated) {
logger.info("Service: 成功級聯(lián)更新考試場次 ID {} 下學員的相關(guān)信息.", id);
} else {
logger.warn("Service: 級聯(lián)更新考試場次 ID {} 下學員信息的操作未被確認或無匹配學員.", id);
}
}
}
}
在這個場景中,策略的選擇是由Spring的依賴注入機制自動完成的。
具體工作原理:
1、策略的收集:
- 由于每個策略類(如
ExamDescriptionUpdateStrategy、ExamStatusUpdateStrategy、ExamStartTimeUpdateStrategy)都標注了@Component注解 - Spring會自動掃描并實例化這些實現(xiàn)了
UpdateExamStrategy接口的類 - 所有策略實例會被自動注入到
ExamServiceImpl中的updateExamStrategies列表中
2、策略的順序:
Spring在注入時會按照以下規(guī)則確定策略在列表中的順序:
- 默認按照類名的字母順序
- 可以通過
@Order注解或?qū)崿F(xiàn)Ordered接口來自定義順序
3、策略的使用:
@Autowired
public ExamServiceImpl(MongoDBUtils mongoDBUtils, List<UpdateExamStrategy> updateExamStrategies) {
this.mongoDBUtils = mongoDBUtils;
this.updateExamStrategies = updateExamStrategies; // Spring自動注入所有策略
}
4、遍歷執(zhí)行:
for (UpdateExamStrategy strategy : updateExamStrategies) {
strategy.applyUpdate(existingExam, examUpdateDTO, examUpdateMap, studentUpdateMap);
}
- 每次遍歷會取出列表中的一個策略實例
- 每個策略實例都是一個具體的策略類的對象(如
ExamDescriptionUpdateStrategy的實例) - 調(diào)用策略的
applyUpdate方法時,會執(zhí)行對應(yīng)策略類中的具體實現(xiàn)
這是一個典型的策略模式結(jié)合Spring依賴注入的應(yīng)用,它的好處是:
- 解耦:各個更新策略相互獨立
- 可擴展:添加新的更新策略只需創(chuàng)建新的策略類并添加
@Component注解 - 維護性好:每個策略類負責處理特定的更新邏輯
總結(jié)
這里添加了一個構(gòu)造函數(shù)來注入 mongoDBUtils 和 updateExamStrategies。如果你的項目中 mongoDBUtils 已經(jīng)通過字段上的 @Autowired 注入,那么在構(gòu)造函數(shù)中再次賦值是多余的,但通常無害。Spring 的依賴注入會處理好這些。
主要邏輯已更改為: 初始化 examUpdateMap 和 studentUpdateMap。 遍歷所有注入的 UpdateExamStrategy 策略。 每個策略根據(jù) ExamUpdateDTO 的內(nèi)容,決定是否要向 examUpdateMap(用于更新 Exam 實體本身)或 studentUpdateMap(用于級聯(lián)更新 CompetitionSignUpLog 實體)添加鍵值對。
如果 examUpdateMap 不為空,則更新 Exam 實體。 如果 studentUpdateMap 不為空,則更新相關(guān)的CompetitionSignUpLog 實體。
這種方式將每個字段的更新邏輯(包括是否需要級聯(lián)更新)封裝在各自的策略類中,使得 updateExam 方法本身更加簡潔,并且易于擴展新的更新邏輯。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Validation和Hibernate Validator結(jié)合國際化代碼實例
這篇文章主要介紹了Spring Validation和Hibernate Validator結(jié)合國際化代碼實例,我們需要對請求參數(shù)進行非空、長度、正確性進行校驗, 本文主要講解Spring Validation 和 Hibernate Validator, 同時整合i18n(國際化)實現(xiàn)參數(shù)校驗自動,需要的朋友可以參考下2023-10-10
java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):線性表
這篇文章主要介紹了Java的數(shù)據(jù)解構(gòu)基礎(chǔ),希望對廣大的程序愛好者有所幫助,同時祝大家有一個好成績,需要的朋友可以參考下,希望能給你帶來幫助2021-07-07
Spring獲取Cookie和Session(@CookieValue()和@SessionAttribute())
本文主要介紹了Spring獲取Cookie和Session(@CookieValue()和@SessionAttribute()),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2026-01-01

