工厂+策略模式 实现新增题目
工厂模式
工厂模式是设计模式中的一个基本类型,属于创建型模式。它的核心思想是定义一个用于创建对象的接口,但是让子类决定实例化哪一个类。工厂模式让对象的实例化过程延迟到子类中进行,从而使得代码更具灵活性和扩展性。
主要分类包括:
- 简单工厂模式:提供一个创建对象的静态方法,根据传入的参数决定创建哪种类型的对象。
- 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个产品类。工厂方法使一个类的实例化延迟到其子类。
- 抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而无需指定它们具体的类。
策略模式
策略模式是一种行为设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户,让代码更加灵活和可扩展。
在策略模式中,通常会有一个上下文(Context)类,它引用一个策略接口,而具体策略类则实现了这个接口。上下文类在运行时决定使用哪个策略,从而改变行为。
工厂+策略模式的结合
将工厂模式和策略模式结合起来使用,可以实现非常灵活的设计。具体来说,可以利用工厂模式来创建不同的策略对象,而策略模式则负责定义这些对象的行为。
例如,假设有一个系统需要根据不同的业务规则执行不同的计算逻辑。可以定义一个策略接口(如CalculationStrategy
),然后有多个实现类(如DiscountStrategy
, TaxStrategy
等)。接着,使用工厂模式(如CalculationStrategyFactory
)根据输入的条件(比如业务类型)创建对应的策略对象。这样,系统不仅能够轻松地添加新的计算逻辑(只需增加策略类和更新工厂逻辑),还能在运行时动态选择合适的策略,极大地增强了系统的可维护性和扩展性。
本文将通过工厂+策略模式对刷题社区中新增题目进行设计:
问题所在:
//假设新增题目都写在主流程里面
//我们则需要判断type,单选需要调用单选的service,多选要调用多选的service,这会导致出现一大堆的if语句判别题目情况再分类进入
//因此选取一个策略模式+工厂模式的形式,一个工厂包含了四种题目类型,根据传入的type值做一个映射关系选择处理方法。
//我们则需要判断type,单选需要调用单选的service,多选要调用多选的service,这会导致出现一大堆的if语句判别题目情况再分类进入
//因此选取一个策略模式+工厂模式的形式,一个工厂包含了四种题目类型,根据传入的type值做一个映射关系选择处理方法。
首先我们定义一个枚举类声明不同题目类型情况
package com.jingdianjichi.subject.commom.enums; import lombok.Getter; /** * 分类类型枚举 * * @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; } }
然后定义一个typehandler接口类
package com.jingdianjichi.subject.domain.handler.subject; import com.jingdianjichi.subject.commom.enums.SubjectInfoTypeEnum; import com.jingdianjichi.subject.domain.entiy.SubjectInfoBO; public interface SubjectTypeHandler { /** * 枚举识别 */ SubjectInfoTypeEnum getHandlerType(); /** * 实际的题目的插入 */ void add(SubjectInfoBO subjectInfoBO); /** * 实际的题目的插入 */ // SubjectOptionBO query(int subjectId); }
然后设计我们不同题目的具体实现逻辑
import com.jingdianjichi.subject.infra.basic.service.SubjectRadioService; import javax.annotation.Resource; import java.util.LinkedList; import java.util.List; /** * 单选题目策略类 * * @author :juziweifenda * @date : 2023/7/24 * */ 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.commom.enums.SubjectInfoTypeEnum; import com.jingdianjichi.subject.domain.entiy.SubjectInfoBO; /** * 判断题目策略类 * * @author :juziweifenda * @date : 2023/7/24 * */ public class JudgeTypeHandler implements SubjectTypeHandler { @Override public SubjectInfoTypeEnum getHandlerType() { return SubjectInfoTypeEnum.JUDGE; } @Override public void add(SubjectInfoBO subjectInfoBO) { //单选实际插入逻辑 } }
其他题目逻辑不在赘述展示
工厂方法实现
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); } } }
最后serviceimpl类引入工厂实现映射
package com.jingdianjichi.subject.domain.service.impl; import com.alibaba.fastjson.JSON; import com.jingdianjichi.subject.commom.enums.IsDeletedFlagEnum; import com.jingdianjichi.subject.domain.convert.SubjectInfoConverter; import com.jingdianjichi.subject.domain.entiy.SubjectInfoBO; import com.jingdianjichi.subject.domain.handler.subject.SubjectTypeHandler; import com.jingdianjichi.subject.domain.handler.subject.SubjectTypeHandlerFactory; import com.jingdianjichi.subject.domain.service.SubjectInfoDomainService; import com.jingdianjichi.subject.infra.basic.entity.SubjectInfo; import com.jingdianjichi.subject.infra.basic.entity.SubjectMapping; import com.jingdianjichi.subject.infra.basic.service.SubjectInfoService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.LinkedList; import java.util.List; @Service @Slf4j public class SubjectInfoDomainServiceImpl implements SubjectInfoDomainService { @Resource private SubjectInfoService subjectInfoService; @Resource private SubjectTypeHandlerFactory subjectTypeHandlerFactory; @Override public void add(SubjectInfoBO subjectInfoBO) { if(log.isInfoEnabled()){ log.info("SubjectInfoDomainServiceImpl.add.bo:{}", JSON.toJSONString(subjectInfoBO)); } //假设都写在主流程里面 //需要判断type,单选需要调用单选的service,多选要调用多选的service,这会导致出现一大堆的if语句判别题目情况再分类进入 //因此选取一个策略模式+工厂模式的形式,一个工厂包含了四种题目类型,根据传入的type值做一个映射关系选择处理方法。 SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBoToInfo(subjectInfoBO); subjectInfoService.insert(subjectInfo); //引入工厂 SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfoBO.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)); subjectMapping.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode()); mappingList.add(subjectMapping); //调用service }); }); } }