在软件开发中,设计模式是解决特定问题的通用方案,而工厂模式与策略模式的结合使用,能在特定业务场景下发挥强大的威力。本文将基于新增题目(题目类型有单选、多选、判断、解答)这一业务场景,详细阐述如何运用 Java 中的工厂 + 策略设计模式来实现。
一、设计模式简介
-
工厂模式 工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。其核心思想是定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类。
-
策略模式 策略模式是一种行为型设计模式,它定义了算法家族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。
二、业务场景分析
在对数智教育社区进行重构时,针对其中的刷题模块开展深入分析。该模块涵盖单选、多选、判断、解答等多种题目类型,仅以新增操作为例,不同题型有着截然不同的逻辑要求:单选题需保障选项唯一正确,多选题允许存在多个正确选项,判断题聚焦于对错判定,解答题则着重于答案的详细评估。倘若舍弃合适设计模式,单纯运用 if - else 进行编码,那么随着题目类型不断丰富以及业务逻辑愈发复杂,代码将陷入难以维护与拓展的困境。然而,借助策略模式与工厂模式联袂运用,成功封装内部实现细节,对外呈现统一调用接口,在大幅削减 if/else 业务代码量的同时,令代码的维护与扩展过程变得更为轻松便捷。
三、实战教程
3.1 定义枚举
/*** 题目类型枚举* 1单选 2多选 3判断 4简答*/
@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;}/*** 根据code值获取枚举对象* @param codeVal code值* @return*/public static SubjectInfoTypeEnum getByCode(int codeVal){for(SubjectInfoTypeEnum resultCodeEnum : SubjectInfoTypeEnum.values()){if(resultCodeEnum.code == codeVal){return resultCodeEnum;}}return null;}}
3.2 定义策略接口
创建一个策略接口 SubjectTypeHandler
,该接口旨在定义所有题目类型通用的操作规范。具体而言,它声明了用于识别枚举身份的方法以及执行实际题目插入操作的方法,以此确保不同类型题目在处理时能够遵循统一的标准和流程,便于后续的扩展与维护。
public interface SubjectTypeHandler {/*** 枚举身份的识别*/SubjectInfoTypeEnum getHandlerType();/*** 实际的题目的插入*/void add(SubjectInfoBO subjectInfoBO);}
3.3 实现具体策略
针对每种题目类型,实现具体的策略类,这些类实现了 SubjectTypeHandler接口,并提供了与特定题型相关的具体实现。
3.3.1 单选题策略
/*** 单选题目的策略类*/
@Component
public class RadioTypeHandler implements SubjectTypeHandler {@Resourceprivate SubjectRadioService subjectRadioService;@Overridepublic SubjectInfoTypeEnum getHandlerType() {return SubjectInfoTypeEnum.RADIO;}@Overridepublic void add(SubjectInfoBO subjectInfoBO) {//单选题目的插入List<SubjectRadio> subjectRadioList = new LinkedList<>();subjectInfoBO.getOptionList().forEach(option -> {SubjectRadio subjectRadio = RadioSubjectConverter.INSTANCE.convertBoToEntity(option);subjectRadio.setSubjectId(subjectInfoBO.getId());subjectRadio.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());subjectRadioList.add(subjectRadio);});subjectRadioService.batchInsert(subjectRadioList);}}
3.3.2 多选题策略
/*** 多选题目的策略类*/
@Component
public class MultipleTypeHandler implements SubjectTypeHandler{@Resourceprivate SubjectMultipleService subjectMultipleService;@Overridepublic SubjectInfoTypeEnum getHandlerType() {return SubjectInfoTypeEnum.MULTIPLE;}@Overridepublic void add(SubjectInfoBO subjectInfoBO) {//多选题目的插入List<SubjectMultiple> subjectMultipleList = new LinkedList<>();subjectInfoBO.getOptionList().forEach(option -> {SubjectMultiple subjectMultiple = MultipleSubjectConverter.INSTANCE.convertBoToEntity(option);subjectMultiple.setSubjectId(subjectInfoBO.getId());subjectMultiple.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());subjectMultipleList.add(subjectMultiple);});subjectMultipleService.batchInsert(subjectMultipleList);}}
3.3.3 判断题策略
/*** 判断题目的策略类*/
@Component
public class JudgeTypeHandler implements SubjectTypeHandler{@Resourceprivate SubjectJudgeService subjectJudgeService;@Overridepublic SubjectInfoTypeEnum getHandlerType() {return SubjectInfoTypeEnum.JUDGE;}@Overridepublic void add(SubjectInfoBO subjectInfoBO) {//判断题目的插入SubjectJudge subjectJudge = new SubjectJudge();SubjectAnswerBO subjectAnswerBO = subjectInfoBO.getOptionList().get(0);subjectJudge.setSubjectId(subjectInfoBO.getId());subjectJudge.setIsCorrect(subjectAnswerBO.getIsCorrect());subjectJudge.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());subjectJudgeService.insert(subjectJudge);}}
3.3.4 简答题策略
/*** 简答题目的策略类*/
@Component
public class BriefTypeHandler implements SubjectTypeHandler{@Resourceprivate SubjectBriefService subjectBriefService;@Overridepublic SubjectInfoTypeEnum getHandlerType() {return SubjectInfoTypeEnum.BRIEF;}@Overridepublic void add(SubjectInfoBO subjectInfoBO) {SubjectBrief subjectBrief = BriefSubjectConverter.INSTANCE.convertBoToEntity(subjectInfoBO);subjectBrief.setSubjectId(subjectInfoBO.getId().intValue());subjectBrief.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());subjectBriefService.insert(subjectBrief);}}
3.4创建工厂类
创建了一个工厂类SubjectTypeHandlerFactory,根据不同的题目类型提供相应的处理类实例。它利用 Spring 的依赖注入功能自动收集所有实现了 SubjectTypeHandler
接口的类实例,并在初始化时将它们注册到一个映射表中。当需要处理某个题目时,可以通过题目类型代码获取到对应的处理类实例,从而实现根据不同题目类型进行不同处理的逻辑。
/*** 题目类型工厂*/
@Component
public class SubjectTypeHandlerFactory implements InitializingBean {@Resourceprivate List<SubjectTypeHandler> subjectTypeHandlerList;private Map<SubjectInfoTypeEnum, SubjectTypeHandler> handlerMap = new HashMap<>();public SubjectTypeHandler getHandler(int subjectType) {SubjectInfoTypeEnum subjectInfoTypeEnum = SubjectInfoTypeEnum.getByCode(subjectType);return handlerMap.get(subjectInfoTypeEnum);}@Overridepublic void afterPropertiesSet() throws Exception {for (SubjectTypeHandler subjectTypeHandler : subjectTypeHandlerList) {handlerMap.put(subjectTypeHandler.getHandlerType(), subjectTypeHandler);}}}
这段代码是一个 Spring 组件类,实现了 InitializingBean
接口,主要作用是作为题目类型处理的工厂类,负责根据不同的题目类型提供对应的处理类实例。以下是代码的详细解释:
注解与类定义
-
@Component
:标记该类为 Spring 的一个组件,使 Spring 容器能够自动检测并管理这个类的实例,无需手动创建对象。 -
public class SubjectTypeHandlerFactory implements InitializingBean
:定义了一个名为SubjectTypeHandlerFactory
的公共类,实现了InitializingBean
接口,以便在 Bean 初始化完成后执行特定的初始化逻辑。
属性
-
@Resource
:这是一个依赖注入注解,用于自动注入subjectTypeHandlerList
属性的值。Spring 容器会查找所有实现了SubjectTypeHandler
接口的 Bean,并将它们收集到这个列表中。 -
private List<SubjectTypeHandler> subjectTypeHandlerList
:这是一个私有列表属性,用于存储所有可用的题目类型处理类实例。 -
private Map<SubjectInfoTypeEnum, SubjectTypeHandler> handlerMap = new HashMap<>()
:这是一个私有映射表,用于将题目类型枚举(SubjectInfoTypeEnum
)与对应的处理类实例(SubjectTypeHandler
)关联起来,方便后续根据题目类型快速获取相应的处理类。
方法
-
public SubjectTypeHandler getHandler(int subjectType)
:这是一个公共方法,用于根据给定的题目类型代码(subjectType
)获取对应的处理类实例。它首先调用SubjectInfoTypeEnum.getByCode(subjectType)
方法将整数类型的题目类型代码转换为对应的枚举类型实例(SubjectInfoTypeEnum
),然后从handlerMap
映射表中查找并返回对应的处理类实例。 -
@Override public void afterPropertiesSet() throws Exception
:这个方法覆写了InitializingBean
接口的afterPropertiesSet
方法。当 Spring 容器初始化完该 Bean 的所有属性后,会自动调用这个方法。在这个方法中,它遍历subjectTypeHandlerList
列表中的每个处理类实例,并通过调用subjectTypeHandler.getHandlerType()
方法获取该处理类实例所对应的题目类型枚举,然后将这个枚举和实例作为键值对存入handlerMap
映射表中,从而完成处理类实例与题目类型的关联。
3.5使用示例
在实际业务场景中,当需要新增题目时,可以根据题目类型获取相应的策略对象,并调用其方法完成题目相关操作。
public void add(SubjectInfoBO subjectInfoBO) {//新增题目主表信息SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBoToInfo(subjectInfoBO);subjectInfoService.insert(subjectInfo);//根据不同策略,新增对应的题目信息SubjectTypeHandler subjectTypeHandler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());subjectTypeHandler.add(subjectInfoBO);}
四、总结
通过在题目系统中应用 Java 的工厂 + 策略设计模式,我们实现了以下优点:
-
良好的扩展性 :当需要新增题目类型时,只需添加新的策略类和在工厂类中进行简单扩展,无需修改现有代码的主体结构。
-
代码复用性高 :将每种题型的逻辑封装在独立的策略类中,便于代码的复用和维护。
-
清晰的责任划分 :工厂类负责创建对象,策略类负责定义算法,使得系统职责明确,易于理解和测试。
这种设计模式的组合使用为解决类似具有多种类型变体且每种类型有不同行为逻辑的业务场景提供了一种优雅、灵活且可维护的解决方案。