如何正确实现开闭原则(OCP)以避免模板逻辑新增时频繁修改if-else分支
1. 现有if-else实现不符合开闭原则
开闭原则的核心要求是对扩展开放,对修改关闭,你当前每次新增模板都要改动Pg控制器的doProcessing方法,属于修改已上线的稳定逻辑,很容易引入历史功能bug,完全不符合开闭原则的要求。
另外你现有代码还存在明显逻辑问题:你创建了Template1/Template2实例后,没有将其设置到TemplateInfo中,调用templateProduct.calculateTemplate时使用的还是Spring注入的单例TemplateInfo,大概率会触发空指针异常。同时单例TemplateInfo在多线程请求场景下会出现属性覆盖的并发问题,不建议作为单例Bean托管给Spring。
2. Class.forName反射方案不属于最优实践
这个方案确实可以消除if-else分支,但存在多个明显缺陷:
- 硬编码了类路径前缀
ocp.,后续如果包结构调整,所有实例创建逻辑都会失效 - 反射创建的实例不受Spring生命周期管理,如果你后续需要在
Template实现类中注入其他Spring Bean(如DAO、外部服务)将无法实现,可扩展性很差 - 缺少参数校验机制,传入非法参数会直接抛出类不存在、类型转换错误等运行时异常,排查成本高
- 反射性能弱于直接获取实例,虽然影响不大但属于不必要的性能损耗
3. 适配Spring环境的最优方案
你已经抽取了TemplateClassification公共接口,结合Spring的Bean管理能力可以零if-else实现完全符合开闭原则的逻辑:
改造步骤
- 给所有
TemplateClassification实现类添加@Component注解,自定义Bean名称和tempId一一对应:
@Component("template1") public class Template1 implements TemplateClassification{ @Override public QuesObj processTemplate() { return new QuesObj("Hi I am header 1","Hi I am footer 1"); } } @Component("template2") public class Template2 implements TemplateClassification{ @Override public QuesObj processTemplate() { return new QuesObj("Hi I am header 2","Hi I am footer 2"); } }
- 改造控制器逻辑,直接从Spring上下文获取对应Bean,删除所有if-else:
@RestController class Pg { @Autowired ApplicationContext applicationContext; @Autowired TemplateProduct templateProduct; public QuesObj doProcessing(@RequestParam("tempId") int tempId){ // 校验Bean是否存在,避免非法参数报错 if(!applicationContext.containsBean("template" + tempId)){ throw new IllegalArgumentException("非法的模板ID"); } TemplateClassification template = (TemplateClassification) applicationContext.getBean("template" + tempId); // 每次请求新建TemplateInfo避免并发问题 TemplateInfo templateInfo = new TemplateInfo(); templateInfo.setTemplateClassification(template); return templateProduct.calculateTemplate(templateInfo); } }
效果
后续新增模板时,你只需要新建类实现TemplateClassification接口,添加对应@Component注解即可,不需要修改任何原有代码,完全符合开闭原则,同时可以享受到Spring的全部Bean管理能力。
内容的提问来源于stack exchange,提问作者Beginner
相关产品推荐
相关产品推荐

