如何在要求新功能时不违反开放式原则?

时间:2017-10-27 12:53:34

标签: c# open-closed-principle

我需要在项目中添加新功能,我试图以最佳方式执行此操作。

所以新功能 => 公开关闭原则。我不应该改变现有的代码,对吗?

这是界面:

public interface IEcuStatisticsExportManager
{
    void ExportStatistics(string datasource, bool modifiedOnly, string userName);
}

有一个类已经实现了这个接口:

public class EcuStatisticsExportManager : IEcuStatisticsExportManager
{
    public void ExportStatistics(string datasource, bool includeAll, string userName)
    {
        //Actual behavior of this method allow us to export statistics for 
        //only one data source. We need to extend this by allowing the user 
        //to export statistics for multiple data sources.

        //Another new feature will be an option to export statistics for
        //all data sources we have in the database
    }
}

记住这些事情后,接口必须看起来像这样:

public interface IEcuStatisticsExportManager
{
    void ExportStatistics(string datasource[], bool exportAll, bool modifiedOnly, string userName);
}

我正在用以下事情挣扎我的大脑:

我如何尊重Open Close Principle,因为我无法覆盖我需要新行为的方法?

我将如何尊重原则,因为我必须在界面上进行更改?

请以最好的方式帮助我这样做。

最好的问候。

3 个答案:

答案 0 :(得分:3)

开放封闭原则是在设计项目架构时应该考虑的事情,至少从我谦虚的经验来看,它永远不会涵盖100%的案例。当您需要添加功能时,设计总是有可能不支持此功能,因此必须打破原则。通常大约30%的工作是由设计完成的,其余的工作是黑客攻击设计。总的来说,我会说只有在保持原则的可能和合理的情况下才能保留这一原则。像SOLID和设计模式这样的概念的主要问题是人们总是在寻找经验法则,并且不理解为什么你按照某个规则工作,遵循它可能弊大于利。

Tmho,您应该问自己的问题是,在给定的业务案例中,如果在给定系统中将该规则保留在给定的情况下是否有意义。在这种情况下,除了旧方法之外,将void ExportStatistics(string datasource[], bool exportAll, bool modifiedOnly, string userName);添加到现有对象并使用重载是有意义的,因此新方法将使用旧方法而不修改它,如果可能的话,可能通过获取所有方法如果需要,来自数据库的数据源,在数据源上运行foreach循环并在每个数据源上调用旧方法,然后根据modifiedOnly参数对数据应用必要的更改。这样可以避免许多潜在的错误和测试,因为没有触及实际测试的导出方法。另一方面,在许多情况下,它会导致性能降低,或者可能会阻止您进行事务处理。在这种情况下,性能在这里很重要吗?您的所有出口操作都必须是交易操作吗?如果你以一种方式与另一种方式相比,必须添加和维护多少代码,你是否有人力来维护它?它比在bug和测试中节省你的时间更重要吗?您可以根据具体情况调整这些问题。只有你知道答案,并且只根据这些答案你应该决定。 SOLID不是圣经(因此不应该完全忽略),当且仅当它对你的情况有效时才应该使用它。

正如@ rory.ap所提到的,不改变不同对象在不同解决方案中实现的接口也非常重要,因为这将是一个重大改变 - 这些解决方案无法构建。如果是这种情况,你应该:

  • 创建一个新界面,如@ roy.ap建议
  • 在没有界面的情况下将所需方法添加到对象
  • 与组织中的所有团队协调重大变更,以确保所有其他项目都相应更新。 然后检查构建服务器以查看所有构建方案是否可以成功构建
同样,在这种情况下,正确的选项完全取决于您所处的具体情况和组织。

答案 1 :(得分:2)

您提议的更改将与开放/已结算的委托人发生冲突。打开扩展,关闭以进行修改。您正在修改界面中方法的签名,这是重大更改。部署该更改后,实现该接口的任何现有项目都不起作用。

根据您的需要,您可以创建第二个界面,例如IEcuStatisticsExportManagerExtended并将您的新方法放在那里。然后,类可以根据需要实现一个或两个接口。

答案 2 :(得分:1)

这不是一个包罗万象的答案,但在这种情况下,您可以创建一个重载方法。

public interface IEcuStatisticsExportManager
{
    void ExportStatistics(string datasource, bool modifiedOnly, string userName);
    void ExportStatistics(string[] datasource, bool exportAll, bool modifiedOnly, string userName);
}

单源方法最有可能调用多源方法,将单个输入转换为一个数组。

但为了避免对签名进行进一步更改,我会为新方法使用输入类,例如

public class ExportStatisticsInput
{
    public string[] DataSources {get; set;}
    public bool ExportAll {get; set;}
    public bool ModifiedOnly {get; set;}
    public string UserName {get; set;}        
}

public interface IEcuStatisticsExportManager
{
    void ExportStatistics(string datasource, bool modifiedOnly, string userName);
    void ExportStatistics(ExportStatisticsInput input);
}

这仍然可以解释为对开放/封闭原则的轻微违反,但这并不算太糟糕。您可能只是将一个方法的内容完整地移动到从两个公共方法调用的私有方法中。