在SpringBoot項(xiàng)目中策略模式的使用方式
使用策略模式之前的代碼
@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 {} 下學(xué)員的 startTime/duration.", id);
} else {
logger.warn("Service: 級聯(lián)更新考試場次 ID {} 下學(xué)員的 startTime/duration 操作未被確認(rèn)或無匹配學(xué)員.", id);
}
}
}
}
創(chuàng)建策略接口即策略實(shí)現(xiàn)類
/**
* 更新考試場次信息的策略接口
*/
public interface UpdateExamStrategy {
/**
* 根據(jù)DTO將待更新的字段填充到相應(yīng)的map中。
*
* @param existingExam 當(dāng)前的考試實(shí)體 (可用于比較或特定邏輯)
* @param dto 考試更新DTO
* @param examUpdateMap 用于收集考試實(shí)體自身更新的字段
* @param studentUpdateMap 用于收集需要級聯(lián)更新到學(xué)員報名記錄的字段
*/
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;
}
//...省略其他方法的實(shí)現(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)的學(xué)生信息可能仍需更新
}
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 {} 下學(xué)員的相關(guān)信息.", id);
} else {
logger.warn("Service: 級聯(lián)更新考試場次 ID {} 下學(xué)員信息的操作未被確認(rèn)或無匹配學(xué)員.", id);
}
}
}
}
在這個場景中,策略的選擇是由Spring的依賴注入機(jī)制自動完成的。
具體工作原理:
1、策略的收集:
- 由于每個策略類(如
ExamDescriptionUpdateStrategy、ExamStatusUpdateStrategy、ExamStartTimeUpdateStrategy)都標(biāo)注了@Component注解 - Spring會自動掃描并實(shí)例化這些實(shí)現(xiàn)了
UpdateExamStrategy接口的類 - 所有策略實(shí)例會被自動注入到
ExamServiceImpl中的updateExamStrategies列表中
2、策略的順序:
Spring在注入時會按照以下規(guī)則確定策略在列表中的順序:
- 默認(rèn)按照類名的字母順序
- 可以通過
@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);
}
- 每次遍歷會取出列表中的一個策略實(shí)例
- 每個策略實(shí)例都是一個具體的策略類的對象(如
ExamDescriptionUpdateStrategy的實(shí)例) - 調(diào)用策略的
applyUpdate方法時,會執(zhí)行對應(yīng)策略類中的具體實(shí)現(xiàn)
這是一個典型的策略模式結(jié)合Spring依賴注入的應(yīng)用,它的好處是:
- 解耦:各個更新策略相互獨(dú)立
- 可擴(kuò)展:添加新的更新策略只需創(chuàng)建新的策略類并添加
@Component注解 - 維護(hù)性好:每個策略類負(fù)責(zé)處理特定的更新邏輯
總結(jié)
這里添加了一個構(gòu)造函數(shù)來注入 mongoDBUtils 和 updateExamStrategies。如果你的項(xiàng)目中 mongoDBUtils 已經(jīng)通過字段上的 @Autowired 注入,那么在構(gòu)造函數(shù)中再次賦值是多余的,但通常無害。Spring 的依賴注入會處理好這些。
主要邏輯已更改為: 初始化 examUpdateMap 和 studentUpdateMap。 遍歷所有注入的 UpdateExamStrategy 策略。 每個策略根據(jù) ExamUpdateDTO 的內(nèi)容,決定是否要向 examUpdateMap(用于更新 Exam 實(shí)體本身)或 studentUpdateMap(用于級聯(lián)更新 CompetitionSignUpLog 實(shí)體)添加鍵值對。
如果 examUpdateMap 不為空,則更新 Exam 實(shí)體。 如果 studentUpdateMap 不為空,則更新相關(guān)的CompetitionSignUpLog 實(shí)體。
這種方式將每個字段的更新邏輯(包括是否需要級聯(lián)更新)封裝在各自的策略類中,使得 updateExam 方法本身更加簡潔,并且易于擴(kuò)展新的更新邏輯。
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
如何在Netty中注解使用Service或者M(jìn)apper
這篇文章主要介紹了如何在Netty中注解使用Service或者M(jìn)apper,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
Java8實(shí)現(xiàn)Stream流的合并的方法展示
本文介紹了Java8中Stream流的合并方法,包括concat()、flatMap()和reduce()三種方法。其中,concat()方法可以將兩個Stream流合并成一個,flatMap()方法可以將一個Stream流中的元素映射成多個Stream流并合并成一個,reduce()方法可以將Stream流中的元素逐個合并成一個結(jié)果2023-05-05
Spring Validation和Hibernate Validator結(jié)合國際化代碼實(shí)例
這篇文章主要介紹了Spring Validation和Hibernate Validator結(jié)合國際化代碼實(shí)例,我們需要對請求參數(shù)進(jìn)行非空、長度、正確性進(jìn)行校驗(yàn), 本文主要講解Spring Validation 和 Hibernate Validator, 同時整合i18n(國際化)實(shí)現(xiàn)參數(shù)校驗(yàn)自動,需要的朋友可以參考下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()),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2026-01-01

