开闭固原则条件失败

Posted

技术标签:

【中文标题】开闭固原则条件失败【英文标题】:Open close solid principle conditional fails 【发布时间】:2021-08-12 20:24:17 【问题描述】:

根据SOLID的开闭原则,类对扩展开放,对修改关闭。

所以我可以根据新的 if-else 条件添加新逻辑吗?

如果我不使用条件,那么我将如何根据哪个条件确定必须应用哪个操作

public interface TemplateClassification 
    QuesObj processTemplate(RawPart rawPart);

public class Template1 implements  TemplateClassification
    
    @Override
    public QuesObj processTemplate(RawPart rawPart) 
        return new QuesObj("Hi header 1"+rawPart.getHead(),"Hi I am footer 1"+rawPart.getFoot());
    



public class Template2 implements  TemplateClassification
    @Override
    public QuesObj processTemplate(RawPart rawPart) 
        return new QuesObj("Hi header 2"+rawPart.getHead(),"Hi I am footer "+rawPart.getFoot());
    


public class TemplateInfo 

    private TemplateClassification templateClassification;

    public TemplateClassification getTemplateClassification() 
        return templateClassification;
    

    public void setTemplateClassification(TemplateClassification templateClassification) 
        this.templateClassification = templateClassification;
    

public class TemplateProduct 
    public QuesObj calculateTemplate(TemplateInfo templateInfo,RawPart rawPart)
        QuesObj ques = templateInfo.getTemplateClassification().processTemplate(rawPart);
        return ques;
    

@RestController
class Pg 

    @Autowired
    TemplateInfo templateInfo;

    @Autowired
    TemplateProduct templateProduct;

    public doProcessing(RawPart rawPart)
        QuesObj ques = null;
        if(rawPart.getId() == 1)
          Template1 temp = new Template1(); 
          ques = templateProduct.calculateTemplate(templateInfo,rawPart);
        
        elseIf(rawPart.getId() == 2)
          Template2 temp = new Template2(); 
          ques = templateProduct.calculateTemplate(templateInfo,rawPart);
        
        elseIf(tempId == 3)
        // coming soon
        
    


如何消除 if else 条件,使其遵循开闭原则

【问题讨论】:

【参考方案1】:

要在 SOLID 中实现“O”,您可以按照以下步骤操作,其中也包括“S”。

我们将使用多态和继承。

第 1 步:

创建一个位于负责创建 QuesObj 的类前面的接口。我们将需要这个,因为当 id 为 1,2 或 3 时,代码可以接收创建者(子类)。

请务必注意,已识别 QuesObj 是因为它会在您的原始 if 语句中返回,这也是我们被允许继续使用此方法的原因。

public interface QuesObjCreator 
    QuesObj calculate(RawPart rawPart);

第 2 步:

在该类的唯一作用是创建对象。

public class QuesObjCreatorFor1 implements QuesObjCreator 

private TemplateInfo templateInfo;
private TemplateProduct templateProduct;


@Override
public QuesObj calculate(RawPart rawPart) 
    Template1 temp = new Template1();
    return templateProduct.calculateTemplate(templateInfo,rawPart);

public class QuesObjCreatorFor2 implements QuesObjCreator 

private TemplateInfo templateInfo;
private TemplateProduct templateProduct;


@Override
public QuesObj calculate(RawPart rawPart) 
    Template2 temp = new Template2();
    return templateProduct.calculateTemplate(templateInfo,rawPart);

第 3 步:

创建工厂以返回 QuesObjCreator。工厂将返回到您的主要代码/服务。

public class QuesObjectCreatorFactory 

private static final Map<Integer,QuesObjCreator> quesObjCreatorMap = new HashMap<>();

public QuesObjectCreatorFactory() 
    quesObjCreatorMap.put(1,new QuesObjCreatorFor1());
    quesObjCreatorMap.put(2,new QuesObjCreatorFor2());



public static QuesObjCreator getQuesObjCreator(final int number) 
    final QuesObjCreator quesObjCreator = quesObjCreatorMap.get(number);
    if(quesObjCreator == null) 
        throw new IllegalArgumentException("QuesObj for "+number+" does not exist");
    

    return quesObjCreator;

第 4 步:

使用工厂创建 QuesObj

public class Pg 

public void doProcessing(RawPart rawPart)
    final QuesObjCreator quesObjCreator = QuesObjectCreatorFactory.getQuesObjCreator(rawPart.getId());
    QuesObj ques = quesObjCreator.calculate(rawPart);

我们共同实现了跨所有类的单一职责并解耦。

易于维护,因为现在您可以添加更多选项来创建 QuesObj,并且不会更改任何代码,从而实现开放可扩展/封闭可修改。

这一切都归结为拥有创作者的工厂和地图。必须使用所有创建者实例填充地图。使用 Spring,这非常容易,因为 Spring 可以扫描您的项目,查找特定类型的 bean,给您一个 List,然后您可以将其转换为 map。

【讨论】:

您好,感谢您的回答,但我相信 quesObjCreatorMap 的大小在课堂上将始终为 0 秒 QuesObjCreatorFor2 QuesObjCreatorFor2 temp 从未使用过 我相信这也是一种工厂模式。 @Beginner 正确,即来自OP原始代码。【参考方案2】:

您的案例与 SOLID 无关。根据开闭原则,你不能允许在运行时修改你的类,这会破坏它的行为。

在你的情况下,我建议如下:

getId() 方法添加到您的 TemplateClassification 接口。

使每个 TemplateClassification 实现成为一个 bean

添加将为您形成模板映射的 bean

  @Bean
  public Map<Integer, TemplateClassification> templates(List<TemplateClassification> templates) 
  return algorithms.stream()
      .collect(Collectors.toMap(TemplateClassification::getId, Function.identity()));
  

Map&lt;Integter, TemplateClassification&gt; templates 自动连接到您的控制器并通过 id 找到所需的模板。

【讨论】:

以上是关于开闭固原则条件失败的主要内容,如果未能解决你的问题,请参考以下文章

利用开闭原则 (SOLID)

接口不同时实现开闭原则

设计的7大原则

设计的7大原则

设计的7大原则

设计模式