在微服务项目中,策略加工厂模式是一种设计模式,用于解决在不同情况下需要使用不同策略的问题。该模式结合了策略模式和工厂模式的特点,使得系统更具灵活性和可扩展性。
一:策略模式
- 策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装到单独的类中,使得它们可以相互替换。这样可以使得算法的变化独立于使用算法的客户端。在微服务项目中,
- 策略模式可用于处理不同的业务场景或者应用场景,每个策略对应一个具体的算法,系统可以根据需要动态地选择和切换策略。
二:工厂模式
- 工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式,通过工厂方法来创建对象,而不是在客户端直接使用
new
关键字创建对象。 - 在微服务项目中,工厂模式可用于根据条件动态创建对象,封装了对象创建的逻辑,使得系统更加灵活,降低了客户端与具体类的耦合度。
策略加工厂模式将这两种模式结合起来,具体体现在以下几点:
- 定义一个策略接口,包含多个具体策略所需的方法。
- 实现多个具体策略类,每个具体策略类实现策略接口,并提供具体的算法逻辑。
- 定义一个工厂类,负责根据条件选择合适的策略并创建相应的策略对象。
- 在微服务项目中,根据不同的业务需求,通过工厂类选择合适的策略对象,从而实现动态选择算法的功能,使得系统更具弹性和可维护性。
三:小案例
例如:一个刷题业务,需要在题库中新增题目,但是题目分为多选题、简答题、判断题、单选题各个题型。我们如果按照传统的模式去进行该接口的实现的话,会造成代码冗余if else特别多。如果使用工厂+策略这个设计模式的话就会消除很多的if else可以使得代码结构更加的清晰,每个功能和模块都会由明确的职责和接口,易于理解和维护。而且有很好的可拓展性!
首先我们先定义题目类型的枚举
import lombok.Getter;
/**
* 题目类型枚举
* 1单选 2多选 3判断 4简答
* @author: ChickenWing
* @date: 2023/10/3
*/
@Getter
public enum SubjectInfoTypeEnum {
RADIO(1,"单选"),
MULTIPLE(2,"多选"),
JUDGE(3,"判断"),
BRIEF(4,"简答"),
;
public int code;
public String desc;
SubjectInfoTypeEnum(int code, String desc){
this.code = code;
this.desc = desc;
}
public static SubjectInfoTypeEnum getByCode(int codeVal){
for(SubjectInfoTypeEnum resultCodeEnum : SubjectInfoTypeEnum.values()){
if(resultCodeEnum.code == codeVal){
return resultCodeEnum;
}
}
return null;
}
}
我们创建一个接口 来进行枚举身份的识别 以及写上 该题型将要实现的方法如增删改查 我写了个
add的方法 增加单选题目!
import com.jingdianjichi.subject.common.enums.SubjectInfoTypeEnum;
import com.jingdianjichi.subject.domain.entity.SubjectInfoBO;
public interface SubjectTypeHandler {
/**
* 枚举身份的识别
* @return
*/
SubjectInfoTypeEnum getHandlerType();
/**
* 实际的题目的插入
* @param subjectInfoBO
*/
void add(SubjectInfoBO subjectInfoBO);
}
然后我们要根据获得题目枚举的具体类型(题目类型 例如:单选、多选)来创建策略类实现我们上面所写的那个策略接口
我们下面举个例如单选题
import com.jingdianjichi.subject.common.enums.SubjectInfoTypeEnum;
import com.jingdianjichi.subject.domain.convert.RadioSubjectConverter;
import com.jingdianjichi.subject.domain.entity.SubjectInfoBO;
import com.jingdianjichi.subject.infra.basic.entity.SubjectRadio;
import com.jingdianjichi.subject.infra.basic.service.SubjectRadioService;
import com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations.X509SubjectNameResolver;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* 单选的题目的策略类
*/
@Component
public class RadioTypeHandler implements SubjectTypeHandler {
@Resource
private SubjectRadioService subjectRadioService;
@Override
public SubjectInfoTypeEnum getHandlerType() {
return SubjectInfoTypeEnum.RADIO;
}
@Override
public void add(SubjectInfoBO subjectInfoBO) {
//单选题目的插入
List<SubjectRadio> subjectRadioList=new LinkedList<>();
subjectInfoBO.getOptionList().forEach(option->{
SubjectRadio subjectRadio= RadioSubjectConverter.INSTANCE.convertBOToEntity(option);
subjectRadio.setSubjectId(subjectInfoBO.getId());
subjectRadioList.add(subjectRadio);
});
subjectRadioService.batchinsert(subjectRadioList);
}
}
这是多选题的策略类 下面的其他类型的题目的策略类我就不一一赘述了 跟多选题一样 实现那个策略接口。
package com.jingdianjichi.subject.domain.handler.subject;
import com.jingdianjichi.subject.common.enums.SubjectInfoTypeEnum;
import com.jingdianjichi.subject.domain.entity.SubjectInfoBO;
import org.springframework.stereotype.Component;
/**
* 多选的题目的策略类
*/
@Component
public class MultipleTypeHandler implements SubjectTypeHandler{
@Override
public SubjectInfoTypeEnum getHandlerType() {
return SubjectInfoTypeEnum.MULTIPLE;
}
@Override
public void add(SubjectInfoBO subjectInfoBO) {
}
}
如下图所示
然后我们就需要创建一个工厂类
import com.jingdianjichi.subject.common.enums.SubjectInfoTypeEnum;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 题目类型工厂
*/
@Component
public class SubjectTypeHandlerFactory implements InitializingBean {
@Resource
private List<SubjectTypeHandler> subjectTypeHandlerList;
private Map<SubjectInfoTypeEnum,SubjectTypeHandler> handlerMap=new HashMap<>();
public SubjectTypeHandler getHandler(int subjectType){
SubjectInfoTypeEnum subjectInfoTypeEnum=SubjectInfoTypeEnum.getByCode(subjectType);
return handlerMap.get(subjectInfoTypeEnum);
}
@Override
public void afterPropertiesSet() throws Exception {
for (SubjectTypeHandler subjectTypeHandler : subjectTypeHandlerList) {
handlerMap.put(subjectTypeHandler.getHandlerType(),subjectTypeHandler);
}
}
}
工厂类要继承InitializingBean 接口
InitializingBean是spring为bean的初始化提供了一种新的方式,里面只有一个方法afterPropertiesSet,作用就是实现这个接口或者实现了继承InitializingBean的方法的bean都要执行这个方法。
最后我们在Controller层中去实现单选题目的增加
private SubjectInfoDomainService subjectInfoDomainService;
/**
* 新增题目
*
* @return
*/
@PostMapping("/add")
public Result<Boolean> add(@RequestBody SubjectInfoDTO subjectInfoDTO) {
try {
if (log.isDebugEnabled()){
log.info("SubjectController.add.dto:{}", JSON.toJSONString(subjectInfoDTO));
}
//参数校验
Preconditions.checkArgument(!StringUtils.isBlank(subjectInfoDTO.getSubjectName()),"题目名称不能为空");
Preconditions.checkNotNull(subjectInfoDTO.getSubjectDifficult(),"难度不能为空");
Preconditions.checkNotNull(subjectInfoDTO.getSubjectType(),"题目类型为空");
Preconditions.checkNotNull(subjectInfoDTO.getSubjectScore(),"题目分数为空");
Preconditions.checkNotNull(!CollectionUtils.isEmpty(subjectInfoDTO.getCategoryIds()),
"分类id不能为空");
Preconditions.checkNotNull(!CollectionUtils.isEmpty(subjectInfoDTO.getLabelIds()),
"标签id不能为空");
SubjectInfoBO subjectInfoBO = SubjectInfoDTOConverter.INSTANCE.
convertDTOTOBO(subjectInfoDTO);
List<SubjectAnswerBO> subjectAnsweBOList = SubjectAnswerDTOConverter.INSTANCE.
convertListDTOToBO(subjectInfoDTO.getOptionList());
subjectInfoBO.setOptionList(subjectAnsweBOList);
subjectInfoDomainService.add(subjectInfoBO);
return Result.ok(true);
}catch (Exception e){
log.error("SubjectController.add.error:{}",e.getMessage(),e);
return Result.fail("新增题目失败");
}
}
Service层
@Override
public void add(SubjectInfoBO subjectInfoBO) {
if (log.isDebugEnabled()) {
log.info("SubjectInfoDomainServiceImpl.add.dto:{}", JSON.toJSONString(subjectInfoBO));
}
//工厂+策略
SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBOToInfo(subjectInfoBO);
subjectInfoService.insert(subjectInfo);
//
SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
handler.add(subjectInfoBO);
List<Integer> categoryIds = subjectInfoBO.getCategoryIds();
List<Integer> labelIds = subjectInfoBO.getLabelIds();
List<SubjectMapping> mappingList=new LinkedList<>();
categoryIds.forEach(categoryId->{
labelIds.forEach(labelId->{
SubjectMapping subjectMapping=new SubjectMapping();
subjectMapping.setSubjectId(subjectInfo.getId());
subjectMapping.setCategoryId(Long.valueOf(categoryId));
subjectMapping.setLabelId(Long.valueOf(labelId));
mappingList.add(subjectMapping);
});
});
subjectMappingService.batchInsert(mappingList);
}
工厂类SubjectTypeHandlerFactory就相当于帮我们创建了题型对应的策略实例,之后我们只需要调用里面的add方法即可,如果不用工厂模式,我们就需要在代码里手动的创建这个题型对应的策略实例。这个工厂模式,就相当与让工厂类代替我们创建策略实例。
希望大家多多关注我,我会持续更新Spring Boot项目中的干货,有什么不足的欢迎评论区留言和私信留言!第一时间回复,关注我 送Java学习资料以及干货!!!