Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当请求新功能时,如何不违反开闭原则?_C#_Open Closed Principle - Fatal编程技术网

C# 当请求新功能时,如何不违反开闭原则?

C# 当请求新功能时,如何不违反开闭原则?,c#,open-closed-principle,C#,Open Closed Principle,我需要在一个项目中添加一个新的功能,我正试图以最好的方式做到这一点 所以新功能=>开闭原则。我不应该改变现有的代码,对吗 以下是界面: public interface IEcuStatisticsExportManager { void ExportStatistics(string datasource, bool modifiedOnly, string userName); } public class EcuStatisticsExportManager : IEcuStat

我需要在一个项目中添加一个新的功能,我正试图以最好的方式做到这一点

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

以下是界面:

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 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);
}
我的大脑在为以下事情挣扎:

当我不能覆盖我需要新行为的方法时,我如何尊重开-关原则

我将如何尊重原则,因为我将不得不对界面进行更改

请帮助我以最好的方式做这件事


致以最良好的祝愿。

您提议的变更将与未结/未结本金发生冲突。打开以进行扩展,关闭以进行修改。您正在修改界面中方法的签名,这是一个突破性的更改。部署该更改后,实现该接口的现有项目将无法工作


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

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

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);
}

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

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

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

正如@rory.ap所提到的,同样重要的是不要更改不同对象在不同解决方案中实现的接口,因为这将是一个突破性的更改-这些解决方案不会构建。如果是这种情况,您应该:
  • 按照@roy.ap的建议,创建一个新界面
  • 将所需的方法添加到没有接口的对象中
  • 与组织中的所有团队协调突破性的变更,以确保所有其他项目都得到相应的更新。 然后检查生成服务器,查看所有生成场景是否都可以成功生成

同样,在这种情况下,正确的选择完全取决于您所处的具体情况和组织。

您可能希望创建一个“版本化接口”,类似于
IECustaticsExportManagerv2
,这样您就不会破坏依赖于旧行为的现有代码。该类可以继续执行它现在通过旧接口执行的操作,也可以转发到新接口,为新参数传递合理的默认值。最终目标是不破坏依赖于当前行为的任何东西。在您的真实代码中,这是ExportStatistics虚拟的吗?@zxxc否,这就是为什么我说我不能重写该方法。在您的示例2中,新功能似乎没有打破开/关原则。我认为您应该创建两个独立的方法,比如ExportAllStatisticsFromAllDataSources(),exportStatisticsFromList。作为其2个新的单独功能,将使用单独的参数列表并将其添加到现有接口中。例如,您可以继承EcuStatisticsExportManager并创建单独的接口IEcuAllStatisticsExportManager(接口可以扩展或不扩展IEcuStatisti