如何在请求新功能时不违反打开-关闭原则



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

因此新功能=>开关原理。我不应该更改现有的代码,对吗?

这是接口:

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

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

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

当我必须对界面进行更改时,我将如何尊重原则?

请用最好的方式帮我做这件事。

致以最良好的问候。

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

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

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

  • 按照@roy.ap的建议创建一个新接口
  • 将所需的方法添加到没有接口的对象中
  • 与组织中的所有团队协调突破性的更改,以确保所有其他项目都得到相应的更新。然后检查您的构建服务器,以查看所有构建场景都可以成功构建
同样,在这种情况下,正确的选择完全取决于您所处的具体情况和组织。

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

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

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

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

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

最新更新